You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2022/11/13 22:17:03 UTC

[camel-karavan] 03/07: Migrate to Quarkus Quinoa

This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git

commit aec2bdfdd88c83cf09c425ea891412a6dc3e55a9
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Fri Nov 11 17:18:41 2022 -0500

    Migrate to Quarkus Quinoa
---
 .gitignore                                         |    1 +
 karavan-app/pom.xml                                |   47 +-
 karavan-app/src/main/resources/META-INF/.gitignore |    4 -
 .../src/main/resources/application.properties      |    7 +-
 karavan-app/src/main/{webapp => webui}/.gitignore  |    0
 .../src/main/{webapp => webui}/package-lock.json   |    6 +-
 .../src/main/{webapp => webui}/package.json        |    0
 .../src/main/{webapp => webui}/public/favicon.ico  |  Bin
 .../src/main/{webapp => webui}/public/index.html   |    0
 .../public/karavan-logo-light.png                  |  Bin
 .../main/{webapp => webui}/public/manifest.json    |    0
 .../src/main/{webapp => webui}/public/robots.txt   |    0
 .../src/main/{webapp => webui}/src/Logo.tsx        |    0
 .../src/main/{webapp => webui}/src/Main.tsx        |    0
 .../src/main/{webapp => webui}/src/MainLogin.tsx   |    0
 .../src/main/{webapp => webui}/src/MainToolbar.tsx |    0
 .../main/{webapp => webui}/src/api/KaravanApi.tsx  |    0
 .../src/main/{webapp => webui}/src/api/SsoApi.tsx  |    0
 .../src/components/ComponentCard.tsx               |    0
 .../src/components/ComponentModal.tsx              |    0
 .../src/components/ComponentsPage.tsx              |    0
 .../src/config/ConfigurationPage.tsx               |    0
 .../src/dashboard/DashboardPage.tsx                |    0
 .../main/webui/src/designer/KaravanDesigner.tsx    |  131 ++
 .../src/main/webui/src/designer/beans/BeanCard.tsx |   65 +
 .../webui/src/designer/beans/BeanProperties.tsx    |  250 ++++
 .../webui/src/designer/beans/BeansDesigner.tsx     |  164 +++
 .../webui/src/designer/error/ErrorDesigner.tsx     |   74 +
 .../src/designer/exception/ExceptionDesigner.tsx   |   74 +
 .../src/main/webui/src/designer/karavan.css        | 1274 +++++++++++++++++
 .../src/main/webui/src/designer/rest/RestCard.tsx  |   78 +
 .../src/designer/rest/RestConfigurationCard.tsx    |   68 +
 .../main/webui/src/designer/rest/RestDesigner.tsx  |  292 ++++
 .../webui/src/designer/rest/RestMethodCard.tsx     |   66 +
 .../webui/src/designer/rest/RestMethodSelector.tsx |   90 ++
 .../webui/src/designer/route/DslConnections.tsx    |  434 ++++++
 .../main/webui/src/designer/route/DslElement.tsx   |  486 +++++++
 .../webui/src/designer/route/DslProperties.tsx     |  245 ++++
 .../main/webui/src/designer/route/DslSelector.tsx  |  167 +++
 .../webui/src/designer/route/RouteDesigner.tsx     |  405 ++++++
 .../route/property/ComponentParameterField.tsx     |  330 +++++
 .../designer/route/property/DataFormatField.tsx    |  173 +++
 .../designer/route/property/DslPropertyField.tsx   |  695 +++++++++
 .../designer/route/property/ExpressionField.tsx    |  165 +++
 .../route/property/KameletPropertyField.tsx        |  201 +++
 .../designer/route/property/KubernetesSelector.tsx |  243 ++++
 .../src/designer/route/property/ObjectField.tsx    |   94 ++
 .../src/designer/templates/TemplatesDesigner.tsx   |   74 +
 .../src/main/webui/src/designer/utils/CamelUi.tsx  |  524 +++++++
 .../main/webui/src/designer/utils/DslMetaModel.ts  |   82 ++
 .../src/main/webui/src/designer/utils/EventBus.ts  |   61 +
 .../webui/src/designer/utils/KaravanComponents.tsx |   45 +
 .../main/webui/src/designer/utils/KaravanIcons.tsx |  330 +++++
 .../main/webui/src/designer/utils/KubernetesAPI.ts |   19 +
 .../main/webui/src/designer/utils/MediaTypes.ts    | 1485 ++++++++++++++++++++
 .../src/main/{webapp => webui}/src/eip/EipCard.tsx |    0
 .../main/{webapp => webui}/src/eip/EipModal.tsx    |    0
 .../src/main/{webapp => webui}/src/eip/EipPage.tsx |    0
 .../src/main/{webapp => webui}/src/index.css       |    0
 .../src/main/{webapp => webui}/src/index.tsx       |    0
 .../{webapp => webui}/src/kamelets/KameletCard.tsx |    0
 .../src/kamelets/KameletModal.tsx                  |    0
 .../src/kamelets/KameletsPage.tsx                  |    0
 .../src/projects/CreateFileModal.tsx               |    0
 .../{webapp => webui}/src/projects/ProjectInfo.tsx |    0
 .../src/projects/ProjectModels.ts                  |    0
 .../src/projects/ProjectOperations.tsx             |    0
 .../{webapp => webui}/src/projects/ProjectPage.tsx |    0
 .../src/projects/ProjectPageToolbar.tsx            |    0
 .../src/projects/ProjectsPage.tsx                  |    0
 .../src/projects/PropertiesEditor.tsx              |    0
 .../src/projects/PropertiesTable.tsx               |    0
 .../{webapp => webui}/src/projects/UploadModal.tsx |    0
 .../main/{webapp => webui}/src/react-app-env.d.ts  |    0
 .../src/main/{webapp => webui}/tsconfig.json       |    0
 75 files changed, 8899 insertions(+), 50 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8bf83af..60c0191 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,3 +62,4 @@ yarn-error.log*
 **/*.vsix
 karavan-cloud/karavan-secret-real.yaml
 **/*.bak
+/karavan-app/.quinoa/
\ No newline at end of file
diff --git a/karavan-app/pom.xml b/karavan-app/pom.xml
index f557073..723d71f 100644
--- a/karavan-app/pom.xml
+++ b/karavan-app/pom.xml
@@ -144,6 +144,11 @@
             <artifactId>rest-assured</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.quarkiverse.quinoa</groupId>
+            <artifactId>quarkus-quinoa</artifactId>
+            <version>1.2.2</version>
+        </dependency>
     </dependencies>
     <build>
         <resources>
@@ -159,48 +164,6 @@
             </resource>
         </resources>
         <plugins>
-            <plugin>
-                <groupId>com.github.eirslett</groupId>
-                <artifactId>frontend-maven-plugin</artifactId>
-                <version>1.11.2</version>
-                <configuration>
-                    <workingDirectory>${project.basedir}/src/main/webapp</workingDirectory>
-                    <installDirectory>target</installDirectory>
-                </configuration>
-                <executions>
-                    <execution>
-                        <id>install node and npm</id>
-                        <goals>
-                            <goal>install-node-and-npm</goal>
-                        </goals>
-                        <configuration>
-                            <nodeVersion>v18.12.0</nodeVersion>
-                            <npmVersion>8.12.2</npmVersion>
-                        </configuration>
-                    </execution>
-                    <execution>
-                        <id>npm install</id>
-                        <goals>
-                            <goal>npm</goal>
-                        </goals>
-                        <configuration>
-                            <arguments>install</arguments>
-                        </configuration>
-                    </execution>
-                    <execution>
-                        <id>npm run build</id>
-                        <goals>
-                            <goal>npm</goal>
-                        </goals>
-                        <configuration>
-                            <environmentVariables>
-                                <CI>false</CI>
-                            </environmentVariables>
-                            <arguments>run prod</arguments>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
                 <groupId>${quarkus.platform.group-id}</groupId>
                 <artifactId>quarkus-maven-plugin</artifactId>
diff --git a/karavan-app/src/main/resources/META-INF/.gitignore b/karavan-app/src/main/resources/META-INF/.gitignore
deleted file mode 100644
index 5e7d273..0000000
--- a/karavan-app/src/main/resources/META-INF/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Ignore everything in this directory
-*
-# Except this file
-!.gitignore
diff --git a/karavan-app/src/main/resources/application.properties b/karavan-app/src/main/resources/application.properties
index e0efd62..760d34a 100644
--- a/karavan-app/src/main/resources/application.properties
+++ b/karavan-app/src/main/resources/application.properties
@@ -29,6 +29,7 @@ quarkus.infinispan-client.auth-password=password
 # Use BASIC as a Docker for Mac workaround
 quarkus.infinispan-client.client-intelligence=BASIC
 
+
 # Public Dev
 %dev.karavan.auth=public
 %dev.quarkus.oidc.enabled=false
@@ -94,4 +95,8 @@ quarkus.container-image.builder=docker
 quarkus.kubernetes-client.connection-timeout=2000
 quarkus.kubernetes-client.request-timeout=10000
 
-quarkus.swagger-ui.always-include=true
\ No newline at end of file
+quarkus.swagger-ui.always-include=false
+
+quarkus.quinoa.package-manager-install=true 
+quarkus.quinoa.package-manager-install.node-version=16.17.0
+quarkus.quinoa.dev-server.port=3000
\ No newline at end of file
diff --git a/karavan-app/src/main/webapp/.gitignore b/karavan-app/src/main/webui/.gitignore
similarity index 100%
rename from karavan-app/src/main/webapp/.gitignore
rename to karavan-app/src/main/webui/.gitignore
diff --git a/karavan-app/src/main/webapp/package-lock.json b/karavan-app/src/main/webui/package-lock.json
similarity index 99%
rename from karavan-app/src/main/webapp/package-lock.json
rename to karavan-app/src/main/webui/package-lock.json
index 26fe106..7b14c30 100644
--- a/karavan-app/src/main/webapp/package-lock.json
+++ b/karavan-app/src/main/webui/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "karavan",
-  "version": "3.18.5",
+  "version": "3.18.6",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "karavan",
-      "version": "3.18.5",
+      "version": "3.18.6",
       "dependencies": {
         "@monaco-editor/react": "4.3.1",
         "@patternfly/patternfly": "4.215.1",
@@ -40,7 +40,7 @@
       }
     },
     "../../../../karavan-core": {
-      "version": "3.18.5",
+      "version": "3.18.6",
       "license": "Apache-2.0",
       "dependencies": {
         "@types/js-yaml": "^4.0.5",
diff --git a/karavan-app/src/main/webapp/package.json b/karavan-app/src/main/webui/package.json
similarity index 100%
rename from karavan-app/src/main/webapp/package.json
rename to karavan-app/src/main/webui/package.json
diff --git a/karavan-app/src/main/webapp/public/favicon.ico b/karavan-app/src/main/webui/public/favicon.ico
similarity index 100%
rename from karavan-app/src/main/webapp/public/favicon.ico
rename to karavan-app/src/main/webui/public/favicon.ico
diff --git a/karavan-app/src/main/webapp/public/index.html b/karavan-app/src/main/webui/public/index.html
similarity index 100%
rename from karavan-app/src/main/webapp/public/index.html
rename to karavan-app/src/main/webui/public/index.html
diff --git a/karavan-app/src/main/webapp/public/karavan-logo-light.png b/karavan-app/src/main/webui/public/karavan-logo-light.png
similarity index 100%
rename from karavan-app/src/main/webapp/public/karavan-logo-light.png
rename to karavan-app/src/main/webui/public/karavan-logo-light.png
diff --git a/karavan-app/src/main/webapp/public/manifest.json b/karavan-app/src/main/webui/public/manifest.json
similarity index 100%
rename from karavan-app/src/main/webapp/public/manifest.json
rename to karavan-app/src/main/webui/public/manifest.json
diff --git a/karavan-app/src/main/webapp/public/robots.txt b/karavan-app/src/main/webui/public/robots.txt
similarity index 100%
rename from karavan-app/src/main/webapp/public/robots.txt
rename to karavan-app/src/main/webui/public/robots.txt
diff --git a/karavan-app/src/main/webapp/src/Logo.tsx b/karavan-app/src/main/webui/src/Logo.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/Logo.tsx
rename to karavan-app/src/main/webui/src/Logo.tsx
diff --git a/karavan-app/src/main/webapp/src/Main.tsx b/karavan-app/src/main/webui/src/Main.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/Main.tsx
rename to karavan-app/src/main/webui/src/Main.tsx
diff --git a/karavan-app/src/main/webapp/src/MainLogin.tsx b/karavan-app/src/main/webui/src/MainLogin.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/MainLogin.tsx
rename to karavan-app/src/main/webui/src/MainLogin.tsx
diff --git a/karavan-app/src/main/webapp/src/MainToolbar.tsx b/karavan-app/src/main/webui/src/MainToolbar.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/MainToolbar.tsx
rename to karavan-app/src/main/webui/src/MainToolbar.tsx
diff --git a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/api/KaravanApi.tsx
rename to karavan-app/src/main/webui/src/api/KaravanApi.tsx
diff --git a/karavan-app/src/main/webapp/src/api/SsoApi.tsx b/karavan-app/src/main/webui/src/api/SsoApi.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/api/SsoApi.tsx
rename to karavan-app/src/main/webui/src/api/SsoApi.tsx
diff --git a/karavan-app/src/main/webapp/src/components/ComponentCard.tsx b/karavan-app/src/main/webui/src/components/ComponentCard.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/components/ComponentCard.tsx
rename to karavan-app/src/main/webui/src/components/ComponentCard.tsx
diff --git a/karavan-app/src/main/webapp/src/components/ComponentModal.tsx b/karavan-app/src/main/webui/src/components/ComponentModal.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/components/ComponentModal.tsx
rename to karavan-app/src/main/webui/src/components/ComponentModal.tsx
diff --git a/karavan-app/src/main/webapp/src/components/ComponentsPage.tsx b/karavan-app/src/main/webui/src/components/ComponentsPage.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/components/ComponentsPage.tsx
rename to karavan-app/src/main/webui/src/components/ComponentsPage.tsx
diff --git a/karavan-app/src/main/webapp/src/config/ConfigurationPage.tsx b/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/config/ConfigurationPage.tsx
rename to karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
diff --git a/karavan-app/src/main/webapp/src/dashboard/DashboardPage.tsx b/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/dashboard/DashboardPage.tsx
rename to karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
diff --git a/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx b/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
new file mode 100644
index 0000000..a5a9f22
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Badge,
+    PageSection, PageSectionVariants, Tab, Tabs, TabTitleIcon, TabTitleText, Tooltip,
+} from '@patternfly/react-core';
+import './karavan.css';
+import {RouteDesigner} from "./route/RouteDesigner";
+import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
+import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {CamelUi} from "./utils/CamelUi";
+import {BeansDesigner} from "./beans/BeansDesigner";
+import {RestDesigner} from "./rest/RestDesigner";
+import {ErrorDesigner} from "./error/ErrorDesigner";
+import {ExceptionDesigner} from "./exception/ExceptionDesigner";
+import {getDesignerIcon} from "./utils/KaravanIcons";
+
+interface Props {
+    onSave?: (filename: string, yaml: string, propertyOnly: boolean) => void
+    onDisableHelp?: () => void
+    filename: string
+    yaml: string
+    dark: boolean
+    tab?: string
+}
+
+interface State {
+    tab: string
+    integration: Integration
+    key: string
+    propertyOnly: boolean
+    routeDesignerRef?: any
+}
+
+export class KaravanDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        tab: this.props.tab ? this.props.tab : 'routes',
+        integration: this.props.yaml && CamelDefinitionYaml.yamlIsIntegration(this.props.yaml)
+            ? CamelDefinitionYaml.yamlToIntegration(this.props.filename, this.props.yaml)
+            : Integration.createNew(this.props.filename),
+        key: "",
+        propertyOnly: false,
+        routeDesignerRef: React.createRef(),
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.key !== this.state.key) {
+            this.props.onSave?.call(this, this.props.filename, this.getCode(this.state.integration), this.state.propertyOnly);
+        }
+    }
+
+    save = (integration: Integration, propertyOnly: boolean): void => {
+        this.setState({key: Math.random().toString(), integration: integration, propertyOnly: propertyOnly});
+    }
+
+    getCode = (integration: Integration): string => {
+        const clone = CamelUtil.cloneIntegration(integration);
+        return CamelDefinitionYaml.integrationToYaml(clone);
+    }
+
+    getTab(title: string, tooltip: string, icon: string) {
+        const counts = CamelUi.getFlowCounts(this.state.integration);
+        const count = counts.has(icon) && counts.get(icon) ? counts.get(icon) : undefined;
+        const showCount = count && count > 0;
+        return (
+            <Tooltip position={"bottom"}
+                     content={<div>{tooltip}</div>}>
+                <div className="top-menu-item">
+                    <TabTitleIcon>{getDesignerIcon(icon)}</TabTitleIcon>
+                    <TabTitleText>{title}</TabTitleText>
+                    {showCount && <Badge isRead className="count">{counts.get(icon)}</Badge>}
+                </div>
+            </Tooltip>
+
+        )
+    }
+
+    downloadImage(){
+        if(this.state.routeDesignerRef){
+            this.state.routeDesignerRef.current.integrationImageDownload();
+         }
+    }
+
+    render() {
+        const tab = this.state.tab;
+        return (
+            <PageSection variant={this.props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page" isFilled padding={{default: 'noPadding'}}>
+                <Tabs className="main-tabs" activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex.toString()})} style={{width: "100%"}}>
+                    <Tab eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab>
+                    <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab>
+                    <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab>
+                    <Tab eventKey='error' title={this.getTab("Error", "Error Handler", "error")}></Tab>
+                    <Tab eventKey='exception' title={this.getTab("Exceptions", "Exception Clauses per type", "exception")}></Tab>
+                </Tabs>
+                {tab === 'routes' && <RouteDesigner integration={this.state.integration}
+                                                    onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
+                                                    dark={this.props.dark}
+                                                    ref={this.state.routeDesignerRef}/>}
+                {tab === 'rest' && <RestDesigner integration={this.state.integration}
+                                                 onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
+                                                 dark={this.props.dark}/>}
+                {tab === 'beans' && <BeansDesigner integration={this.state.integration}
+                                                   onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
+                                                   dark={this.props.dark}/>}
+                {tab === 'error' && <ErrorDesigner integration={this.state.integration}
+                                                   onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
+                                                   dark={this.props.dark}/>}
+                {tab === 'exception' && <ExceptionDesigner integration={this.state.integration}
+                                                           onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
+                                                           dark={this.props.dark}/>}
+            </PageSection>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx b/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx
new file mode 100644
index 0000000..803272f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {NamedBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+
+interface Props {
+    bean: NamedBeanDefinition
+    selectedStep?: NamedBeanDefinition
+    integration: Integration
+    selectElement: (element: NamedBeanDefinition) => void
+    deleteElement: (element: NamedBeanDefinition) => void
+}
+
+export class BeanCard extends React.Component<Props, any> {
+
+    selectElement = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.selectElement.call(this, this.props.bean);
+    }
+
+    delete = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.deleteElement.call(this, this.props.bean);
+    }
+
+    render() {
+        const bean = this.props.bean;
+        return (
+            <div className={this.props.selectedStep?.uuid === bean.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => this.selectElement(e)}>
+                <div className="header">
+                    <div className="title">BEAN</div>
+                    <div className="title">{bean.name}</div>
+                    <div className="description">{bean.type}</div>
+                    {/*<Tooltip position={"bottom"} content={<div>Add REST method</div>}>*/}
+                        {/*<Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => this.selectMethod(e)} className="add-button">Add method</Button>*/}
+                    {/*</Tooltip>*/}
+                    <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
+                </div>
+                <div className="rest-content" key={Math.random().toString()}>
+
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx b/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
new file mode 100644
index 0000000..78f475f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Form,
+    FormGroup,
+    TextInput, Button, Title, Tooltip, Popover, InputGroup,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import {
+    NamedBeanDefinition,
+} from "karavan-core/lib/model/CamelDefinition";
+import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {SensitiveKeys} from "karavan-core/lib/model/CamelMetadata";
+import {v4 as uuidv4} from "uuid";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import {IntegrationHeader} from "../utils/KaravanComponents";
+import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {KubernetesSelector} from "../route/property/KubernetesSelector";
+import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon";
+import {KubernetesAPI} from "../utils/KubernetesAPI";
+import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
+import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
+
+
+interface Props {
+    integration: Integration
+    bean?: NamedBeanDefinition
+    dark: boolean
+    onChange: (bean: NamedBeanDefinition) => void
+    onClone: (bean: NamedBeanDefinition) => void
+}
+
+interface State {
+    bean?: NamedBeanDefinition
+    properties: Map<string, [string, string, boolean]>
+    key: string,
+    showKubernetesSelector: boolean
+    kubernetesSelectorUuid?: string
+    kubernetesSelectorProperty?: string
+}
+
+export class BeanProperties extends React.Component<Props, State> {
+
+    preparePropertiesMap = (properties: any): Map<string, [string, string, boolean]> => {
+        const result = new Map<string, [string, string, boolean]>();
+        Object.keys(properties).forEach((k, i, a) => result.set(uuidv4(), [k, properties[k], false]));
+        return result;
+    }
+
+    public state: State = {
+        bean: this.props.bean,
+        key: '',
+        showKubernetesSelector: false,
+        properties: this.props.bean?.properties ? this.preparePropertiesMap(this.props.bean?.properties) : new Map<string, [string, string, boolean]>()
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevProps.bean?.uuid !== this.props.bean?.uuid) {
+            this.setBean(this.props.bean);
+        }
+        if (prevState.key !== this.state.key && this.state.bean) {
+            const bean = CamelUtil.cloneBean(this.state.bean);
+            const properties: any = {};
+            this.state.properties.forEach(p => properties[p[0]] = p[1]);
+            bean.properties = properties;
+            this.setState({bean: bean});
+            this.props.onChange?.call(this, bean);
+        }
+    }
+
+    setBean = (bean?: NamedBeanDefinition) => {
+        this.setState({
+            bean: bean,
+            properties: bean?.properties ? this.preparePropertiesMap(bean.properties) : new Map<string, [string, string, false]>()
+        });
+    }
+
+    beanChanged = (fieldId: string, value: string) => {
+        if (this.state.bean) {
+            const bean = CamelUtil.cloneBean(this.state.bean);
+            (bean as any)[fieldId] = value;
+            this.setState({bean: bean});
+            this.props.onChange?.call(this, bean);
+        }
+    }
+
+    propertyChanged = (uuid: string, key: string, value: string, showPassword: boolean) => {
+        this.setState(state => {
+            state.properties.set(uuid, [key, value, showPassword]);
+            return {properties: state.properties, key: Math.random().toString()};
+        })
+    }
+
+    propertyDeleted = (uuid: string) => {
+        this.setState(state => {
+            state.properties.delete(uuid);
+            return {properties: state.properties, key: Math.random().toString()};
+        })
+    }
+
+    selectKubernetes = (value: string) => {
+        const propertyId = this.state.kubernetesSelectorProperty;
+        const uuid = this.state.kubernetesSelectorUuid;
+        if (propertyId && uuid){
+            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
+            this.propertyChanged(uuid, propertyId, value, false);
+            this.setState({showKubernetesSelector: false, kubernetesSelectorProperty: undefined})
+        }
+    }
+
+    openKubernetesSelector = (uuid: string, propertyName: string) => {
+        this.setState({kubernetesSelectorUuid: uuid, kubernetesSelectorProperty: propertyName, showKubernetesSelector: true});
+    }
+
+    closeKubernetesSelector = () => {
+        this.setState({showKubernetesSelector: false})
+    }
+
+    getKubernetesSelectorModal() {
+        return (
+            <KubernetesSelector
+                dark={false}
+                isOpen={this.state.showKubernetesSelector}
+                onClose={() => this.closeKubernetesSelector()}
+                onSelect={this.selectKubernetes}/>)
+    }
+
+    cloneBean = () => {
+        if (this.state.bean) {
+            const bean = CamelUtil.cloneBean(this.state.bean);
+            bean.uuid = uuidv4();
+            this.props.onClone?.call(this, bean);
+        }
+    }
+
+    getLabelIcon = (displayName: string, description: string) => {
+        return (
+            <Popover
+                    position={"left"}
+                    headerContent={displayName}
+                    bodyContent={description}
+                    footerContent={
+                        <div>
+                            <b>Required</b>
+                        </div>
+                    }>
+                    <button type="button" aria-label="More info" onClick={e => {
+                        e.preventDefault();
+                        e.stopPropagation();
+                    }} className="pf-c-form__group-label-help">
+                        <HelpIcon noVerticalAlign/>
+                    </button>
+                </Popover>
+        )
+    }
+
+    getBeanForm() {
+        const bean = this.state.bean;
+        return (
+            <>
+                <div className="headers">
+                    <div className="top">
+                        <Title headingLevel="h1" size="md">Bean</Title>
+                        <Tooltip content="Clone bean" position="bottom">
+                            <Button variant="link" onClick={() => this.cloneBean()} icon={<CloneIcon/>}/>
+                        </Tooltip>
+                    </div>
+                </div>
+                <FormGroup label="Name" fieldId="name" isRequired labelIcon={this.getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
+                               onChange={e => this.beanChanged("name", e)}/>
+                </FormGroup>
+                <FormGroup label="Type" fieldId="type" isRequired labelIcon={this.getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type} onChange={e => this.beanChanged("type", e)}/>
+                </FormGroup>
+                <FormGroup label="Properties" fieldId="properties" className="bean-properties">
+                    {Array.from(this.state.properties.entries()).map((v, index, array) => {
+                        const i = v[0];
+                        const key = v[1][0];
+                        const value = v[1][1];
+                        const showPassword = v[1][2];
+                        const isSecret = key !== undefined && SensitiveKeys.includes(key.toLowerCase());
+                        return (
+                            <div key={"key-" + i} className="bean-property">
+                                <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id="key" name="key" value={key}
+                                           onChange={e => this.propertyChanged(i, e, value, showPassword)}/>
+                                <InputGroup>
+                                    {KubernetesAPI.inKubernetes &&
+                                        <Tooltip position="bottom-end" content="Select value from Kubernetes">
+                                        <Button variant="control" onClick={e => this.openKubernetesSelector(i, key)}>
+                                            <KubernetesIcon/>
+                                        </Button>
+                                    </Tooltip>}
+                                    <TextInput
+                                        placeholder="Bean Field Value"
+                                        type={isSecret && !showPassword ? "password" : "text"}
+                                        className="text-field"
+                                        isRequired
+                                        id="value"
+                                        name="value"
+                                        value={value}
+                                        onChange={e => this.propertyChanged(i, key, e, showPassword)}/>
+                                    {isSecret && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
+                                        <Button variant="control" onClick={e => this.propertyChanged(i, key, value, !showPassword)}>
+                                            {showPassword ? <ShowIcon/> : <HideIcon/>}
+                                        </Button>
+                                    </Tooltip>}
+                                </InputGroup>
+                                <Button variant="link" className="delete-button" onClick={e => this.propertyDeleted(i)}><DeleteIcon/></Button>
+                            </div>
+                        )
+                    })}
+                    <Button variant="link" className="add-button" onClick={e => this.propertyChanged(uuidv4(), '', '', false)}>
+                        <AddIcon/>Add property</Button>
+                </FormGroup>
+            </>
+        )
+    }
+
+    render() {
+        return (
+            <div className='properties' key={this.state.bean ? this.state.bean.uuid : 'integration'}>
+                <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
+                    {this.state.bean === undefined && <IntegrationHeader integration={this.props.integration}/>}
+                    {this.state.bean !== undefined && this.getBeanForm()}
+                </Form>
+                {this.getKubernetesSelectorModal()}
+            </div>
+        )
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx b/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
new file mode 100644
index 0000000..d48ea68
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Modal, PageSection
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {NamedBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUi} from "../utils/CamelUi";
+import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {BeanProperties} from "./BeanProperties";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {BeanCard} from "./BeanCard";
+
+interface Props {
+    onSave?: (integration: Integration, propertyOnly: boolean) => void
+    integration: Integration
+    dark: boolean
+}
+
+interface State {
+    integration: Integration
+    showDeleteConfirmation: boolean
+    selectedBean?: NamedBeanDefinition
+    key: string
+    propertyOnly: boolean
+}
+
+export class BeansDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: this.props.integration,
+        showDeleteConfirmation: false,
+        key: "",
+        propertyOnly: false
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.key !== this.state.key) {
+            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
+        }
+    }
+
+    showDeleteConfirmation = (bean: NamedBeanDefinition) => {
+        this.setState({selectedBean: bean, showDeleteConfirmation: true});
+    }
+
+    onIntegrationUpdate = (i: Integration) => {
+        this.setState({integration: i, propertyOnly: false, showDeleteConfirmation: false, key: Math.random().toString()});
+    }
+
+    deleteBean = () => {
+        const i = CamelDefinitionApiExt.deleteBeanFromIntegration(this.state.integration, this.state.selectedBean);
+        this.setState({
+            integration: i,
+            showDeleteConfirmation: false,
+            key: Math.random().toString(),
+            selectedBean: new NamedBeanDefinition(),
+            propertyOnly: false
+        });
+    }
+
+    changeBean = (bean: NamedBeanDefinition) => {
+        const clone = CamelUtil.cloneIntegration(this.state.integration);
+        const i = CamelDefinitionApiExt.addBeanToIntegration(clone, bean);
+        this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedBean: bean});
+    }
+
+    getDeleteConfirmation() {
+        return (<Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={this.state.showDeleteConfirmation}
+            onClose={() => this.setState({showDeleteConfirmation: false})}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => this.deleteBean()}>Delete</Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+            ]}
+            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            <div>
+                Delete bean from integration?
+            </div>
+        </Modal>)
+    }
+
+    selectBean = (bean?: NamedBeanDefinition) => {
+        this.setState({selectedBean: bean})
+    }
+
+    unselectBean = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+        if ((evt.target as any).dataset.click === 'BEANS') {
+            evt.stopPropagation()
+            this.setState({selectedBean: undefined})
+        }
+    };
+
+    createBean = () => {
+        this.changeBean(new NamedBeanDefinition());
+    }
+
+    getPropertiesPanel() {
+        return (
+            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
+                <BeanProperties integration={this.props.integration}
+                                bean={this.state.selectedBean}
+                                dark={this.props.dark}
+                                onChange={this.changeBean}
+                                onClone={this.changeBean}/>
+            </DrawerPanelContent>
+        )
+    }
+
+    render() {
+        const beans = CamelUi.getBeans(this.state.integration);
+        return (
+            <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
+                <div className="rest-page-columns">
+                    <Drawer isExpanded isInline>
+                        <DrawerContent panelContent={this.getPropertiesPanel()}>
+                            <DrawerContentBody>
+                                <div className="graph" data-click="REST"  onClick={event => this.unselectBean(event)}>
+                                    <div className="flows">
+                                        {beans?.map(bean => <BeanCard key={bean.uuid + this.state.key}
+                                                                      selectedStep={this.state.selectedBean}
+                                                                      bean={bean}
+                                                                      integration={this.props.integration}
+                                                                      selectElement={this.selectBean}
+                                                                      deleteElement={this.showDeleteConfirmation}/>)}
+                                        <div className="add-rest">
+                                            <Button
+                                                variant={beans?.length === 0 ? "primary" : "secondary"}
+                                                data-click="ADD_REST"
+                                                icon={<PlusIcon/>}
+                                                onClick={e => this.createBean()}>Create new bean
+                                            </Button>
+                                        </div>
+                                    </div>
+                                </div>
+                            </DrawerContentBody>
+                        </DrawerContent>
+                    </Drawer>
+                </div>
+                {this.getDeleteConfirmation()}
+            </PageSection>
+        )
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/error/ErrorDesigner.tsx b/karavan-app/src/main/webui/src/designer/error/ErrorDesigner.tsx
new file mode 100644
index 0000000..ef11836
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/error/ErrorDesigner.tsx
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    EmptyState, EmptyStateBody, EmptyStateIcon,
+    PageSection, Title
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import CubesIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon';
+
+interface Props {
+    onSave?: (integration: Integration, propertyOnly: boolean) => void
+    integration: Integration
+    dark: boolean
+}
+
+interface State {
+    integration: Integration
+    selectedStep?: CamelElement
+    key: string
+    propertyOnly: boolean
+}
+
+export class ErrorDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: this.props.integration,
+        key: "",
+        propertyOnly: false
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.key !== this.state.key) {
+            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
+        }
+    }
+
+    onIntegrationUpdate = (i: Integration) => {
+        this.setState({integration: i, key: Math.random().toString()});
+    }
+
+    render() {
+        return (
+            <PageSection className="error-page" isFilled padding={{default: 'noPadding'}}>
+                <div className="error-page-columns">
+                    <EmptyState>
+                        <EmptyStateIcon icon={CubesIcon} />
+                        <Title headingLevel="h4" size="lg">
+                            Error handler
+                        </Title>
+                        <EmptyStateBody>
+                            Error handler not implemented yet
+                        </EmptyStateBody>
+                    </EmptyState>
+                </div>
+            </PageSection>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/exception/ExceptionDesigner.tsx b/karavan-app/src/main/webui/src/designer/exception/ExceptionDesigner.tsx
new file mode 100644
index 0000000..d3b39d8
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/exception/ExceptionDesigner.tsx
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+     EmptyState, EmptyStateBody, EmptyStateIcon,
+    PageSection, Title
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import CubesIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon';
+
+interface Props {
+    onSave?: (integration: Integration, propertyOnly: boolean) => void
+    integration: Integration
+    dark: boolean
+}
+
+interface State {
+    integration: Integration
+    selectedStep?: CamelElement
+    key: string
+    propertyOnly: boolean
+}
+
+export class ExceptionDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: this.props.integration,
+        key: "",
+        propertyOnly: false
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.key !== this.state.key) {
+            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
+        }
+    }
+
+    onIntegrationUpdate = (i: Integration) => {
+        this.setState({integration: i, key: Math.random().toString()});
+    }
+
+    render() {
+        return (
+            <PageSection className="exception-page" isFilled padding={{default: 'noPadding'}}>
+                <div className="exception-page-columns">
+                    <EmptyState>
+                        <EmptyStateIcon icon={CubesIcon} />
+                        <Title headingLevel="h4" size="lg">
+                            Exception Clauses
+                        </Title>
+                        <EmptyStateBody>
+                            Exception Clauses not implemented yet
+                        </EmptyStateBody>
+                    </EmptyState>
+                </div>
+            </PageSection>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-app/src/main/webui/src/designer/karavan.css
new file mode 100644
index 0000000..6d8c687
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/karavan.css
@@ -0,0 +1,1274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+.karavan .tools-section {
+    padding-top: 0px;
+    padding-bottom: 0px;
+    padding-right: 0;
+    border-bottom: 1px solid #eee;
+}
+
+.karavan .tools-section .dsl-title {
+    display: flex;
+    flex-direction: row;
+}
+
+.karavan .tools-section .dsl-title .title {
+    margin-right: 16px;
+}
+
+.karavan .brand {
+    height: 36px;
+}
+
+.karavan .header-button {
+    margin-left: var(--pf-c-page__header-tools--MarginRight);
+}
+
+.karavan .page-header {
+    display: flex;
+    justify-content: space-between;
+}
+
+.karavan .page-header .top-toolbar .pf-c-page__header-tools{
+    margin-right: 0;
+}
+.karavan .page-header .top-toolbar {
+    width: 100%;
+}
+
+.karavan .page-header .pf-c-page__header-brand {
+    padding-right: 16px;
+}
+
+.karavan .main-alert {
+    position: fixed;
+    width: 500px;
+    height: 90px;
+    left: 50%;
+    margin-top: 20px;
+    margin-left: -250px;
+}
+
+.filler {
+    width: 100%;
+}
+
+.modal-footer {
+    width: 100%;
+}
+
+.modal-footer .deploy-buttons .pf-c-form__actions {
+    justify-content: flex-end;
+    margin-top: 16px;
+}
+
+.modal-footer .deploy-buttons .pf-c-button {
+    margin-left: 16px;
+}
+
+.action-buttons .pf-c-form__actions {
+    justify-content: flex-end;
+    margin-top: 16px;
+}
+
+.action-buttons .pf-c-button {
+    margin-left: 16px;
+}
+
+.pf-c-modal-box__footer .hidden {
+    display: none;
+}
+
+.upload-buttons .pf-c-form__actions {
+    margin-top: 20px;
+    justify-content: flex-end;
+}
+
+/*integration page*/
+.integration-page .integration-card {
+    cursor: pointer;
+    height: 160px;
+}
+
+.integration-page .integration-card .pf-c-card__header {
+    padding-right: 6px;
+}
+
+.integration-page .integration-card .icon {
+    height: 24px;
+}
+
+.integration-page .integration-card .delete-button {
+    font-size: 17px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: 0;
+    background: transparent;
+    color: #b1b1b7;
+    visibility: hidden;
+}
+
+.integration-page .integration-card:hover .delete-button {
+    visibility: visible;
+}
+
+/*kamelets*/
+.kamelets-page .kamelet-card {
+    cursor: pointer;
+    height: 160px;
+}
+
+.kamelets-page .kamelet-card .pf-c-card__header {
+    padding-right: 6px;
+}
+
+.kamelets-page .kamelet-card .pf-c-card__body {
+    overflow: hidden;
+    position: relative;
+}
+
+.kamelets-page .kamelet-card .icon {
+    height: 24px;
+    margin-top: auto;
+    margin-bottom: auto;
+    border: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -o-user-select: none;
+    user-select: none;
+}
+
+/*kamelet modal*/
+.kamelet-modal-card .pf-c-card__header {
+    padding-right: 6px;
+    display: flex;
+    justify-content: space-between;
+}
+
+.kamelet-modal-card .pf-c-card__title {
+    font-weight: 600;
+}
+
+.kamelet-modal-card .description {
+    overflow: hidden;
+    position: relative;
+    max-width: 600px;
+}
+
+.kamelet-modal-card .icon {
+    height: 36px;
+    margin-top: auto;
+    margin-bottom: auto;
+    border: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -o-user-select: none;
+    user-select: none;
+}
+
+.karavan .page {
+    height: 100%;
+    width: 100%;
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+}
+
+/*DSL*/
+.karavan .dsl-page {
+    flex: 1;
+    overflow: auto;
+}
+
+.karavan .dsl-page .dsl-page-columns {
+    display: block;
+    height: 100%;
+    background: #fafafa;
+}
+
+.karavan .top-icon {
+    height: 24px;
+}
+
+.karavan .main-tabs .top-menu-item {
+    display: flex;
+    flex-direction: row;
+}
+
+.karavan .main-tabs .top-menu-item .count {
+    background: var(--pf-global--active-color--100);
+    color: white;
+    height: fit-content;
+    margin-top: auto;
+    margin-bottom: auto;
+    min-width: 0px;
+}
+
+.karavan .main-tabs .pf-c-tabs__link .pf-c-tabs__item-icon {
+    height: 24px;
+    margin-right: 0;
+}
+
+.karavan .main-tabs .pf-c-tabs__item-text {
+    font-size: 14px;
+    font-weight: bold;
+    margin-top: auto;
+    margin-bottom: auto;
+    margin-right: 6px;
+}
+
+/*Properties*/
+.karavan .properties {
+    border: 1px solid #eee;
+    padding: 10px 10px 10px 10px;
+    font-size: 14px;
+    background: #fcfcfc;
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    margin-bottom: 70px;
+}
+
+.karavan .pf-c-drawer__splitter {
+    width: 2px;
+    background-color: #fcfcfc;
+}
+
+.karavan .pf-c-drawer__splitter-handle {
+    display: none;
+}
+
+.karavan .properties .headers {
+    grid-row-gap: 10px;
+    row-gap: 10px;
+    display: contents;
+}
+
+.karavan .properties .headers .top {
+    width: 100%;
+    display: flex;
+    flex-direction: row;
+}
+
+.karavan .properties .headers .top h1{
+    width: 100%;
+    margin-top: auto;
+    margin-bottom: auto;
+}
+
+.karavan .properties .footer {
+    height: 100%;
+    display: contents;
+}
+
+.karavan .properties .pf-c-form {
+    row-gap: 10px;
+}
+
+.karavan .properties .pf-c-form__group-label {
+    padding-bottom: 3px;
+    display: flex;
+    justify-content: space-between;
+}
+
+.karavan .properties .pf-c-form__label {
+    font-size: 14px;
+}
+
+.karavan .properties .text-field {
+    font-size: 14px;
+    height: auto;
+}
+
+.karavan .properties .pf-c-select {
+    --pf-c-select__toggle--FontSize: 14px;
+    --pf-c-select__menu-item--FontSize: 14px;
+}
+
+.karavan .properties .input-group .pf-c-text-input-group__text {
+    width: 100%;
+}
+
+.karavan .properties .input-group .pf-c-chip-group,
+.karavan .properties .input-group .pf-c-text-input-group__main,
+.karavan .properties .input-group .pf-c-chip-group .pf-c-chip-group__list,
+.karavan .properties .input-group .pf-c-chip-group .pf-c-chip-group__list .pf-c-chip-group__list-item,
+.karavan .properties .input-group .pf-c-chip-group .pf-c-chip-group__main {
+    display:block;
+}
+
+.karavan .properties .input-group .pf-c-chip-group {
+    margin-left: 0;
+}
+
+.karavan .properties .input-group .pf-c-chip-group .pf-c-chip .pf-c-chip__text{
+    max-width: inherit;
+}
+.karavan .properties .input-group .pf-c-chip-group .pf-c-chip {
+    width: 100%;
+}
+
+.karavan .properties .input-group .pf-c-text-input-group__utilities {
+    align-items: end;
+    margin-top: auto;
+}
+
+.karavan .properties .chip .pf-c-button{
+    position:absolute;
+    right: 0;
+}
+
+.karavan .properties .expression-title {
+    font-size: 17px;
+    font-weught: bold;
+}
+
+.karavan .properties .text-area {
+    font-size: 13px;
+}
+
+.karavan .properties .pf-c-select__menu-search {
+    padding: 0px 6px 6px 6px;
+}
+
+.karavan .properties .pf-c-select__toggle-typeahead {
+    font-size: 14px;
+    height: auto;
+}
+
+.karavan .properties .pf-c-select__menu-item {
+    /*width: 280px;*/
+}
+
+.karavan .properties .pf-c-select__menu-item-description {
+    overflow-wrap: anywhere;
+}
+
+.karavan .properties .number {
+    display: flex;
+    justify-content: space-between;
+}
+
+.karavan .properties .number .number-property {
+    width: 100%;
+}
+
+.karavan .properties .number .clear-button {
+    color: #b1b1b7;
+    --pf-c-button--BorderRadius: var(--pf-c-button--m-control--BorderRadius);
+    --pf-c-button--disabled--BackgroundColor: var(--pf-c-button--m-control--disabled--BackgroundColor);
+    --pf-c-button--after--BorderWidth: var(--pf-c-button--m-control--after--BorderWidth);
+    --pf-c-button--after--BorderColor: var(--pf-c-button--m-control--after--BorderTopColor) var(--pf-c-button--m-control--after--BorderRightColor) var(--pf-c-button--m-control--after--BorderBottomColor) var(--pf-c-button--m-control--after--BorderLeftColor);
+    color: var(--pf-c-button--m-control--Color);
+    background-color: var(--pf-c-button--m-control--BackgroundColor);
+    padding-left: 5px;
+    padding-right: 5px;
+}
+
+.karavan .properties .help-button {
+    font-size: 12px;
+}
+
+.karavan .properties .component-selector {
+    border-width: var(--pf-global--BorderWidth--sm);
+    border-top-color: var(--pf-global--BorderColor--300);
+    border-right-color: var(--pf-global--BorderColor--300);
+    border-bottom-color: var(--pf-global--BorderColor--200);
+    border-left-color: var(--pf-global--BorderColor--300);
+    border-style: solid;
+}
+
+.karavan .properties .object-value {
+    display: flex;
+    flex-direction: row;
+    margin-bottom: 3px;
+}
+
+.karavan .properties .object-key-value .object {
+    padding-top: 0;
+}
+
+.karavan .properties .object-value .delete-button {
+    margin: 0;
+    padding: 5px 3px 0px 6px;
+    height: 16px;
+    color: #909090;
+}
+
+.karavan .properties .object-key-value,
+.karavan .properties .object-key-value .object-field {
+    display: flex;
+    flex-direction: row;
+    gap: 3px;
+}
+
+.karavan .properties .object-key-value .delete-button {
+    margin: auto 0 22px 0;
+    padding: 0px 0px 0px 3px;
+    height: 16px;
+    color: #909090;
+}
+
+.karavan .properties .expression,
+.karavan .properties .object,
+.karavan .properties .dataformat,
+.karavan .properties .parameters {
+    padding-top: 6px;
+    padding-left: 16px;
+    row-gap: 6px;
+    display: grid;
+    width: 100%;
+}
+
+.karavan .properties .expression .pf-c-form__group-label,
+.karavan .properties .object .pf-c-form__group-label,
+.karavan .properties .dataformat .pf-c-form__group-label,
+.karavan .properties .parameters .pf-c-form__group-label {
+    font-weight: 100;
+}
+
+.karavan .properties .expression .pf-c-form__group,
+.karavan .properties .object .pf-c-form__group,
+.karavan .properties .dataformat .pf-c-form__group,
+.karavan .properties .parameters .pf-c-form__group {
+    margin-bottom: 10px;
+}
+
+.karavan .properties .expression .pf-c-select__menu-wrapper,
+.karavan .properties .object .pf-c-select__menu-wrapper,
+.karavan .properties .dataformat .pf-c-select__menu-wrapper,
+.karavan .properties .parameters .pf-c-select__menu-wrapper{
+    width: 350px;
+}
+
+.karavan .properties .add-button {
+    font-size: 15px;
+    height: 15px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: auto auto auto 6px;
+    background: transparent;
+}
+
+.karavan .properties .add-button svg {
+    margin-right: 6px;
+}
+
+.karavan .properties .pf-c-expandable-section__toggle {
+    margin: 0;
+    padding: 0;
+}
+
+.karavan .properties .pf-c-expandable-section__content {
+    margin: 0;
+}
+
+.karavan .properties .pf-c-expandable-section__content p {
+    min-height: 6px;
+}
+
+
+/*Graph*/
+.karavan .dsl-page .graph {
+    display: block;
+    flex-direction: column;
+    font-size: 14px;
+    height: 100%;
+    width: 100%;
+    position: relative;
+    overflow-y: auto;
+}
+
+.karavan .dsl-page .flows {
+    width: 100%;
+    position: relative;
+    margin-bottom: 80px;
+}
+
+.karavan .dsl-page .flows .add-flow {
+    margin-top: 16px;
+    display: flex;
+    justify-content: center;
+}
+
+/*connections*/
+.karavan .dsl-page .graph .connections {
+    position: absolute;
+    top: 0;
+    left: 0;
+    background: transparent;
+}
+
+.karavan .dsl-page .graph .connections .icon {
+    height: 20px;
+}
+
+.karavan .dsl-page .graph .connections .arrow {
+    stroke: var(--pf-global--Color--200);
+    fill: var(--pf-global--Color--200);
+}
+
+.karavan .dsl-page .graph .connections .path {
+    stroke: var(--pf-global--Color--200);
+    stroke-width: 1;
+    fill: transparent;
+}
+
+.karavan .dsl-page .graph .connections .circle-outgoing,
+.karavan .dsl-page .graph .connections .circle-incoming {
+    stroke: var(--pf-global--Color--200);
+    stroke-width: 1;
+    fill: white;
+}
+
+.karavan .dsl-page .graph .connections .path-incoming {
+    stroke-dasharray: 5;
+    -webkit-animation: dashdraw .5s linear infinite;
+    animation: dashdraw .5s linear infinite;
+    stroke: var(--pf-global--Color--200);
+    stroke-width: 1;
+    fill: transparent;
+}
+
+.karavan .dsl-page .graph .connections .path-direct {
+    stroke-dasharray: 0;
+    stroke: var(--pf-global--Color--200);
+    stroke-width: 0.7;
+    stroke-opacity: 0.7;
+    fill: transparent;
+}
+
+.karavan .dsl-page .graph .connections .path-direct-selected {
+    stroke-dasharray: 0;
+    stroke: var(--pf-global--active-color--100);
+    stroke-width: 1;
+    stroke-opacity: 1;
+    fill: transparent;
+}
+
+.karavan .dsl-page .graph .connections .path-seda {
+    stroke-dasharray: 2;
+    stroke: var(--pf-global--Color--200);
+    -webkit-animation: dashdraw .5s linear infinite;
+    animation: dashdraw .5s linear infinite;
+    stroke-width: 1;
+    fill: transparent;
+}
+
+@keyframes dashdraw{0%{stroke-dashoffset:10}}
+
+.karavan .dsl-page .flows .step-element {
+    align-items: center;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    width: fit-content;
+    border-color: var(--pf-global--Color--200);
+    border-radius: 16px;
+    border-width: 1px;
+    min-width: 120px;
+    padding: 3px 4px 4px 4px;
+    margin: 3px auto 0 auto;
+    position: relative;
+}
+
+.karavan {
+    --step-border-color: var(--pf-global--Color--200);
+    --step-border-color-selected: var(--pf-global--active-color--100);
+}
+
+.karavan .dsl-page .flows .children {
+    display: flex;
+    flex-wrap: wrap;
+    flex-wrap:nowrap;
+    gap: 6px;
+}
+
+.karavan .dsl-page .flows .has-child {
+    display: flex;
+    flex-direction: column;
+    flex-wrap:nowrap;
+    gap: 6px;
+}
+
+.karavan .dsl-page .flows .step-element .header-route {
+    display: block;
+    border: none;
+    background: transparent;
+    padding: 0px 0px 0px 0px;
+    margin-bottom: 10px;
+    min-width: 160px;
+    z-index: 101;
+}
+
+.karavan .step-element .header-route .delete-button {
+    position: absolute;
+    top: 4px;
+    right: 4px;
+    font-size: 14px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: 0;
+    background: transparent;
+    color: #909090;
+    visibility: hidden;
+}
+
+.karavan .step-element .header .delete-button,
+.element-builder .header .delete-button {
+    position: absolute;
+    top: -5px;
+    font-size: 14px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: 0 0 0 10px;
+    background: transparent;
+    color: #909090;
+    visibility: hidden;
+}
+
+/*.karavan .step-element:hover .delete-button,*/
+/*.karavan .step-element:hover .delete-button,*/
+.karavan .step-element .header:hover .delete-button,
+.karavan .step-element .header-route:hover .delete-button,
+.element-builder .header:hover .delete-button {
+    visibility: visible;
+}
+
+.modal-delete {
+    width: 350px !important;
+}
+
+.karavan .step-element .header {
+    height: 50px;
+}
+
+.karavan .step-element-selected {
+    background-color: rgba(var(--pf-global--palette--blue-50), 1);
+}
+
+.karavan .step-element .selected .header-icon {
+    border-color: var(--pf-global--primary-color--100);
+    background-color: var(--pf-global--palette--blue-50);
+    border-width: 2px;
+}
+
+.karavan .step-element .header .header-text {
+    position: absolute;
+    top: 8px;
+    left: 0;
+    width: 100%;
+    display: flex;
+    flex-direction: row;
+}
+.karavan .step-element .header .spacer {
+    width: 50%
+}
+.karavan .step-element .header .text-bottom {
+    background-color: rgba(255, 255, 255, 0.5);
+}
+.karavan .step-element .header .text-right {
+    padding-left: 17px;
+    width: 50%;
+    text-align:start;
+}
+
+.karavan .step-element .header .header-text-required {
+    color: var(--pf-global--danger-color--100);
+    font-weight: bold;
+}
+
+.karavan .step-element .header-icon {
+    border-color: var(--pf-global--Color--200);
+    border-style: solid;
+    border-radius: 30px;
+    border-width: 1px;
+    background: white;
+    width: 30px;
+    height: 30px;
+    margin: auto;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.karavan .step-element .header .icon,
+.element-builder .header .icon {
+    height: 20px;
+    width: auto;
+    border: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -o-user-select: none;
+    user-select: none;
+}
+
+.karavan .step-element .add-element-button {
+    top: 5px;
+    left: 5px;
+    font-size: 15px;
+    height: 15px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    background: transparent;
+    color: var(--pf-global--primary-color--100);
+    visibility: hidden;
+    z-index: 100;
+    position: absolute;
+}
+
+.karavan .step-element .insert-element-button {
+    position: absolute;
+    top: -5px;
+    /*right: 10px;*/
+    font-size: 14px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: 0 0 0 -25px;
+    background: transparent;
+    background: transparent;
+    color: var(--pf-global--primary-color--100);
+    visibility: hidden;
+    z-index: 100;
+}
+
+.karavan .step-element .header:hover .insert-element-button {
+    visibility: visible;
+}
+
+.karavan .step-element .add-button-bottom {
+    position: relative;
+}
+
+.karavan .step-element .add-button-left {
+    position: absolute;
+    top: 4px;
+    left: 4px;
+}
+
+.karavan .step-element .add-button {
+    font-size: 15px;
+    height: 15px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: 0;
+    background: transparent;
+    color: var(--pf-global--primary-color--100);
+    visibility: hidden;
+    z-index: 100;
+}
+
+.karavan .dsl-page .flows .hidden-step {
+    display: none;
+}
+
+.element-builder .add-button {
+    top: 0;
+    right: 5px;
+    font-size: 15px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: 3px auto 0 auto;
+    background: transparent;
+    color: var(--pf-global--primary-color--100);
+    visibility: hidden;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-end;
+    width: 16px;
+}
+
+.element-builder .add-button svg {
+    margin: 0 auto 3px auto;
+}
+
+
+.element-builder:hover .add-button,
+.karavan .step-element:hover .add-element-button,
+.karavan .step-element:hover .add-button {
+    visibility: visible;
+}
+
+.dsl-gallery {
+    margin-top: 16px;
+}
+
+.dsl-modal {
+    height: 80%;
+    color: var(--pf-global--Color--100);
+}
+
+.dsl-modal .dsl-card {
+    cursor: pointer;
+}
+
+.dsl-modal .dsl-card .icon {
+    height: 20px;
+    width: auto;
+    border: none;
+    margin-right: 6px;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -o-user-select: none;
+    user-select: none;
+}
+
+.dsl-modal .search {
+    width: 300px;
+    position: absolute;
+    top: 24px;
+    right: 72px;
+}
+
+
+.dsl-modal .search .pf-c-form__group-label{
+    padding: 0;
+    margin: auto;
+}
+
+.dsl-modal .search .text-field {
+    font-size: 14px;
+    height: auto;
+}
+
+.dsl-modal .pf-c-form.pf-m-horizontal .pf-c-form__group {
+    display: contents;
+}
+
+.dsl-modal .pf-c-card__body {
+    padding-bottom: 0;
+}
+
+.dsl-modal .pf-c-card__footer {
+    padding-bottom: 1em;
+}
+
+.dsl-modal .footer {
+    display: flex;
+    flex-direction: row;
+    margin-top: 6px;
+    flex-wrap: wrap;
+}
+
+.dsl-modal .version {
+    opacity: 0.5;
+    white-space: nowrap;
+    margin-left: auto;
+}
+
+.dsl-modal .support-level {
+    position: absolute;
+    right: 6px;
+    top: 6px;
+    white-space: nowrap;
+}
+
+.dsl-modal .labels {
+    opacity: 0.5;
+}
+
+.move-modal {
+    width: 270px !important;
+}
+
+.move-modal .pf-c-modal-box__body {
+    margin-right: 0 !important;
+}
+.move-modal .pf-m-plain {
+    display: none;
+}
+
+.yaml-code {
+    overflow: auto;
+    height: 100%;
+    width: 100%;
+    padding-bottom: 100px;
+}
+
+.pf-c-popover__footer {
+    overflow-wrap: anywhere;
+}
+
+/*REST Page*/
+.karavan .rest-page {
+    flex: 1;
+    overflow: auto;
+}
+
+.karavan .rest-page .rest-page-columns {
+    display: block;
+    height: 100%;
+    background: #fafafa;
+}
+
+.karavan .rest-page .graph {
+    display: block;
+    flex-direction: column;
+    font-size: 14px;
+    height: 100%;
+    width: 100%;
+    position: relative;
+    overflow-y: auto;
+}
+
+.karavan .rest-page .flows {
+    width: 800px;
+    margin: 0 auto 80px auto;
+}
+
+.karavan .rest-page .flows .add-flow {
+    margin-top: 16px;
+    display: flex;
+    justify-content: center;
+}
+
+.karavan .rest-page .rest-config-card,
+.karavan .rest-page .rest-card,
+.karavan .rest-page .method-card {
+    border-style: dotted;
+    border-radius: 4px;
+    border-width: 1px;
+    padding: 0 6px 0 6px;
+    margin-bottom: 6px;
+    position: relative;
+}
+
+.karavan .rest-page .method-card .rest-method-desc {
+    display: flex;
+    flex-direction: column;
+}
+
+.karavan .rest-page .rest-card-unselected,
+.karavan .rest-page .rest-config-card-unselected,
+.karavan .rest-page .method-card-unselected {
+    border-color: var(--pf-global--Color--200);
+    background-color: transparent;
+}
+
+.karavan .rest-page .rest-card-selected,
+.karavan .rest-page .rest-config-card-selected,
+.karavan .rest-page .method-card-selected {
+    border-color: var(--pf-global--primary-color--100);
+}
+
+.karavan .rest-page .rest-card-selected .title {
+    color: var(--pf-global--primary-color--100);
+}
+
+.karavan .rest-page .rest-config-card,
+.karavan .rest-page .rest-card {
+    display: flex;
+    flex-direction: column;
+    margin-top: 16px;
+}
+
+.karavan .rest-page .header {
+    display: flex;
+    flex-direction: row;
+    gap: 16px;
+    height: 44px;
+    margin-left: 6px;
+    cursor: pointer;
+    ustify-content: space-between;
+}
+
+.karavan .rest-page .rest-config-card,
+.karavan .rest-page .method-card {
+    display: flex;
+    flex-direction: row;
+    gap: 16px;
+    height: 44px;
+    cursor: pointer;
+}
+
+.karavan .rest-page .method-card .method {
+    margin: auto 0 auto 0;
+    border-radius: 3px;
+    color: #fff;
+    font-family: sans-serif;
+    font-size: 14px;
+    font-weight: 700;
+    min-width: 80px;
+    padding: 6px 0;
+    text-align: center;
+    text-shadow: 0 1px 0 rgb(0 0 0 / 10%);
+}
+
+.karavan .rest-page .method-card-unselected .method {
+    background: var(--pf-global--Color--400);
+}
+
+.karavan .rest-page .method-card-selected .method {
+    background: var(--pf-global--primary-color--100);
+}
+
+.karavan .rest-page .rest-card .title,
+.karavan .rest-page .rest-config-card .title,
+.karavan .rest-page .method-card .title {
+    margin: auto 0 auto 0;
+    font-weight: bold;
+    white-space: nowrap;
+}
+
+.karavan .rest-page .rest-card .description,
+.karavan .rest-page .rest-config-card .description,
+.karavan .rest-page .method-card .description {
+    margin: auto 0 auto 0;
+    width: 670px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+.karavan .rest-page .rest-config-card .delete-button,
+.karavan .rest-page .rest-card .delete-button,
+.karavan .rest-page .method-card .delete-button {
+    position: absolute;
+    top: 3px;
+    right: 3px;
+    font-size: 14px;
+    line-height: 1;
+    border: 0;
+    padding: 0;
+    margin: 0;
+    background: transparent;
+    color: #909090;
+    visibility: hidden;
+    z-index: 100;
+}
+
+.karavan .rest-page .rest-config-card:hover .delete-button,
+.karavan .rest-page .rest-card:hover .delete-button,
+.karavan .rest-page .method-card:hover .delete-button {
+    visibility: visible;
+}
+
+.karavan .rest-page .rest-card .add-button {
+    font-size: 15px;
+    border: 0;
+    background: transparent;
+    color: var(--pf-global--primary-color--100);
+    z-index: 100;
+}
+
+.karavan .rest-page .add-rest {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    margin-top: 16px;
+    gap: 6px;
+}
+
+/*Beans*/
+.karavan .rest-page .properties .bean-properties .pf-c-form__group-control {
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+}
+
+.karavan .rest-page .properties .bean-property {
+    display: flex;
+    flex-direction: row;
+    gap: 3px;
+}
+
+.karavan .rest-page .properties .bean-property .delete-button {
+    padding: 3px;
+    color: #b1b1b7;
+    font-size: 14px;
+}
+
+/*YAML*/
+.karavan .yaml-page {
+    height: 100px;
+}
+/*Exception*/
+/*Template*/
+/*Error*/
+
+.karavan .templates-page,
+.karavan .exception-page,
+.karavan .error-page{
+    flex: 1;
+    overflow: auto;
+}
+
+.karavan .templates-page-columns,
+.karavan .exception-page-columns,
+.karavan .error-page-columns {
+    height: 100%;
+    background: #fafafa;
+}
+
+.karavan .templates-page-columns .pf-c-empty-state,
+.karavan .exception-page-columns .pf-c-empty-state,
+.karavan .error-page-columns .pf-c-empty-state {
+    margin: auto;
+    height: 100%;
+}
+
+/* Help */
+.dont-show {
+    margin-right: auto;
+}
+
+/*Catalogues*/
+.karavan .kamelets-page .kamelet-card .pf-c-card__footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+}
+
+.karavan .tools-section .tools .header {
+    display: flex;
+    flex-direction: row;
+}
+.karavan .tools-section .tools .header .labels {
+    height: fit-content;
+    margin-left: 3px;
+}
+
+/* Project Tools */
+.karavan .project-builder {
+    height: 100%;
+    font-size: 14px;
+}
+
+.karavan .project-builder .tools-section {
+    padding-left: 10px;
+    padding-right: 10px;
+}
+
+.karavan .project-builder .pf-c-toolbar__content {
+    padding: 0;
+}
+
+.karavan .project-builder .pf-c-tabs__link,
+.karavan .project-builder .pf-c-card__title,
+.karavan .project-builder .profile-caption
+{
+    font-size: 14px;
+}
+
+.karavan .project-builder .card-header svg {
+    margin-right: 6px;
+}
+
+.karavan .project-builder .card-disabled {
+    opacity: 0.4;
+}
+
+.karavan .project-builder .pf-c-form__label {
+    font-size: 14px;
+}
+
+.karavan .project-builder .pf-c-check__label {
+    font-size: 14px;
+}
+
+.karavan .project-builder .text-field {
+    font-size: 14px;
+    height: auto;
+}
+
+.karavan .project-builder .pf-c-input-group button {
+    font-size: 14px;
+    height: auto;
+}
+
+.karavan .project-builder .pf-c-form {
+    --pf-c-form--GridGap: 1em;
+}
+
+.karavan .project-builder .pf-c-card__body {
+    --pf-c-card--child--PaddingRight: 0.5em;
+    --pf-c-card--child--PaddingBottom: 1em;
+    --pf-c-card--child--PaddingLeft: 1em;
+}
+
+.karavan .project-builder .pf-c-card__header {
+    --pf-c-card--first-child--PaddingTop: 0.5em;
+}
+
+.karavan .project-builder .center {
+    height: 100%;
+    width: 100%;
+    display: flex;
+    flex-direction: row;
+    gap: 10px;
+    padding: 10px;
+}
+.karavan .project-builder .center-column {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+}
+
+.karavan .project-builder .footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    padding: 10px;
+}
+
+.karavan .project-builder .footer .progress {
+    flex-grow: 4;
+}
+
+.karavan .project-builder .footer .buttons {
+    height: 60px;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+}
+.karavan .project-builder .project-properties td {
+    padding: 0;
+    margin: 0;
+}
+.karavan .project-builder .project-properties td,
+.karavan .project-builder .project-properties tr {
+    border: none;
+}
+.karavan .project-builder .project-properties .delete-button {
+    padding: 0 0 0 6px;
+    margin: 0;
+}
+.karavan .project-builder .add-button {
+    width: fit-content;
+    margin-top: 6px;
+}
+
+.root .tooltip-required-field .pf-c-tooltip__content {
+    text-align: start;
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx b/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx
new file mode 100644
index 0000000..e4e487f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button, Tooltip
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {RestDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {RestMethodCard} from "./RestMethodCard";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+
+interface Props {
+    rest: RestDefinition
+    selectedStep?: CamelElement
+    integration: Integration
+    selectMethod: (element: CamelElement) => void
+    selectElement: (element: CamelElement) => void
+    deleteElement: (element: CamelElement) => void
+}
+
+export class RestCard extends React.Component<Props, any> {
+
+    selectElement = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.selectElement.call(this, this.props.rest);
+    }
+
+    selectMethod = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.selectMethod.call(this, this.props.rest);
+    }
+
+    delete = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.deleteElement.call(this, this.props.rest);
+    }
+
+    render() {
+        const rest = this.props.rest;
+        return (
+            <div className={this.props.selectedStep?.uuid === rest.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => this.selectElement(e)}>
+                <div className="header">
+                    <div className="title">REST</div>
+                    <div className="title">{rest.path}</div>
+                    <div className="description">{rest.description}</div>
+                    <Tooltip position={"bottom"} content={<div>Add REST method</div>}>
+                        <Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => this.selectMethod(e)} className="add-button">Add method</Button>
+                    </Tooltip>
+                    <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
+                </div>
+                <div className="rest-content" key={Math.random().toString()}>
+                    {rest.get?.map(get => <RestMethodCard key={get.uuid} method={get} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
+                    {rest.post?.map(post => <RestMethodCard key={post.uuid} method={post} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
+                    {rest.put?.map(put => <RestMethodCard key={put.uuid} method={put} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
+                    {rest.patch?.map(patch => <RestMethodCard key={patch.uuid} method={patch} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
+                    {rest.delete?.map(del => <RestMethodCard key={del.uuid} method={del} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
+                    {rest.head?.map(head => <RestMethodCard key={head.uuid} method={head} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx b/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx
new file mode 100644
index 0000000..c05dc4f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {Button} from '@patternfly/react-core';
+import '../karavan.css';
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import {RestConfigurationDefinition} from "karavan-core/lib/model/CamelDefinition";
+
+interface Props {
+    restConfig: RestConfigurationDefinition
+    selectedRestConfig?: CamelElement
+    integration: Integration
+    selectElement: (element: CamelElement) => void
+    deleteElement: (element: CamelElement) => void
+}
+
+interface State {
+    restConfig: RestConfigurationDefinition
+    expanded: boolean
+}
+
+export class RestConfigurationCard extends React.Component<Props, State> {
+
+    public state: State = {
+        restConfig: this.props.restConfig,
+        expanded: false
+    };
+
+    selectElement = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.selectElement.call(this, this.state.restConfig);
+    }
+
+    delete = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.deleteElement.call(this, this.props.restConfig);
+    }
+
+    render() {
+        const restConfig = this.state.restConfig;
+        const desc = restConfig.host && restConfig.port
+            ? restConfig.host + ":" + restConfig.port
+            : (restConfig.host ? restConfig.host : "") + (restConfig.port ? restConfig.port : "");
+        return (
+            <div className={this.props.selectedRestConfig?.uuid === restConfig.uuid ? "rest-config-card rest-config-card-selected" : "rest-config-card rest-config-card-unselected"} onClick={e => this.selectElement(e)}>
+                <div className="title">Configuration</div>
+                <div className="title">{restConfig.contextPath}</div>
+                <div className="description">{desc}</div>
+                <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
+            </div>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx b/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
new file mode 100644
index 0000000..bc32cc9
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Modal,
+    PageSection
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {DslProperties} from "../route/DslProperties";
+import {RouteToCreate} from "../utils/CamelUi";
+import {RestCard} from "./RestCard";
+import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {RestConfigurationDefinition, RestContextRefDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {RestMethodSelector} from "./RestMethodSelector";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {RestConfigurationCard} from "./RestConfigurationCard";
+import {v4 as uuidv4} from "uuid";
+
+interface Props {
+    onSave?: (integration: Integration, propertyOnly: boolean) => void
+    integration: Integration
+    dark: boolean
+}
+
+interface State {
+    integration: Integration
+    selectedStep?: CamelElement
+    key: string
+    showSelector: boolean
+    showDeleteConfirmation: boolean
+    propertyOnly: boolean
+}
+
+export class RestDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: this.props.integration,
+        key: "",
+        showSelector: false,
+        showDeleteConfirmation: false,
+        propertyOnly: false
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.key !== this.state.key) {
+            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
+        }
+    }
+
+    onIntegrationUpdate = (i: Integration) => {
+        this.setState({integration: i, showSelector: false, key: Math.random().toString(), propertyOnly: false});
+    }
+
+    selectElement = (element: CamelElement) => {
+        this.setState({selectedStep: element})
+    }
+
+    onPropertyUpdate = (element: CamelElement, newRoute?: RouteToCreate) => {
+        if (newRoute) {
+            let i = CamelDefinitionApiExt.updateIntegrationRestElement(this.state.integration, element);
+            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName + ":" + newRoute.name})
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            this.setState({
+                integration: clone,
+                key: Math.random().toString(),
+                showSelector: false,
+                selectedStep: element,
+                propertyOnly: false
+            });
+        } else {
+            const clone = CamelUtil.cloneIntegration(this.state.integration);
+            const i = CamelDefinitionApiExt.updateIntegrationRestElement(clone, element);
+            this.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
+        }
+    }
+
+    unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+        if ((evt.target as any).dataset.click === 'REST') {
+            evt.stopPropagation()
+            this.setState({selectedStep: undefined,})
+        }
+    };
+
+    addRest = (rest: RestDefinition) => {
+        const clone = CamelUtil.cloneIntegration(this.state.integration);
+        const i = CamelDefinitionApiExt.addRestToIntegration(clone, rest);
+        this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedStep: rest});
+    }
+
+    createRest = () => {
+        this.addRest(new RestDefinition());
+    }
+
+    createRestConfiguration = () => {
+        this.addRest(new RestConfigurationDefinition());
+    }
+
+    showDeleteConfirmation = (element: CamelElement) => {
+        this.setState({selectedStep: element, showSelector: false, showDeleteConfirmation: true});
+    }
+
+    deleteElement = () => {
+        const step = this.state.selectedStep;
+        if (step) {
+            let i;
+            if (step.dslName === 'RestDefinition') i = CamelDefinitionApiExt.deleteRestFromIntegration(this.state.integration, step.uuid);
+            else if (step.dslName === 'RestConfigurationDefinition') i = CamelDefinitionApiExt.deleteRestConfigurationFromIntegration(this.state.integration);
+            else i = CamelDefinitionApiExt.deleteRestMethodFromIntegration(this.state.integration, step.uuid);
+            this.setState({
+                integration: i,
+                showSelector: false,
+                showDeleteConfirmation: false,
+                key: Math.random().toString(),
+                selectedStep: undefined,
+                propertyOnly: false
+            });
+        }
+    }
+
+    getDeleteConfirmation() {
+        return (<Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={this.state.showDeleteConfirmation}
+            onClose={() => this.setState({showDeleteConfirmation: false})}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => this.deleteElement()}>Delete</Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+            ]}
+            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            <div>
+                Delete element from integration?
+            </div>
+        </Modal>)
+    }
+
+    closeMethodSelector = () => {
+        this.setState({showSelector: false})
+    }
+
+    onMethodSelect = (method: DslMetaModel) => {
+        if (this.state.selectedStep) {
+            const clone = CamelUtil.cloneIntegration(this.state.integration);
+            const m = CamelDefinitionApi.createStep(method.dsl, {});
+            const i = CamelDefinitionApiExt.addRestMethodToIntegration(clone, m, this.state.selectedStep?.uuid);
+            this.setState({integration: i, key: Math.random().toString(), selectedStep: m, showSelector: false});
+        }
+    }
+
+    cloneRest = (rest: CamelElement) => {
+        if (rest.dslName === 'RestDefinition'){
+            const cloneRest = CamelUtil.cloneStep(rest);
+            cloneRest.uuid = uuidv4();
+            const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration);
+            const i = CamelDefinitionApiExt.addRestToIntegration(cloneIntegration, cloneRest);
+            this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedStep: cloneRest});
+        } else if (rest.dslName === 'RestConfigurationDefinition') {
+            // could be only one RestConfigurationDefinition
+        } else if (this.state.selectedStep) {
+            const parentId = CamelDefinitionApiExt.findRestMethodParent(this.state.integration, rest);
+            if (parentId){
+                const cloneRest = CamelUtil.cloneStep(rest);
+                cloneRest.uuid = uuidv4();
+                const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration);
+                const i = CamelDefinitionApiExt.addRestMethodToIntegration(cloneIntegration, cloneRest, parentId);
+                this.setState({integration: i, key: Math.random().toString(), selectedStep: cloneRest, showSelector: false});
+            }
+        }
+    }
+
+    selectMethod = (element: CamelElement) => {
+        this.setState({selectedStep: element, showSelector: true})
+    }
+
+    getSelectorModal() {
+        return (
+            <Modal
+                title="Select method"
+                width={'90%'}
+                className='dsl-modal'
+                isOpen={this.state.showSelector}
+                onClose={() => this.closeMethodSelector()}
+                actions={{}}>
+                <RestMethodSelector
+                    dark={this.props.dark}
+                    onMethodSelect={this.onMethodSelect}/>
+            </Modal>)
+    }
+
+    getRestConfigurationCard(config: RestContextRefDefinition) {
+        return (<>
+            <RestConfigurationCard key={Math.random().toString()}
+                                   selectedRestConfig={this.state.selectedStep}
+                                   restConfig={config}
+                                   integration={this.props.integration}
+                                   selectElement={this.selectElement}
+                                   deleteElement={this.showDeleteConfirmation}/>
+        </>)
+    }
+
+    getRestCards(data: RestDefinition[]) {
+        return (<>
+            {data?.map(rest => <RestCard key={rest.uuid + this.state.key}
+                                         selectedStep={this.state.selectedStep}
+                                         rest={rest}
+                                         integration={this.props.integration}
+                                         selectMethod={this.selectMethod}
+                                         selectElement={this.selectElement}
+                                         deleteElement={this.showDeleteConfirmation}/>)}
+        </>)
+    }
+
+
+    getPropertiesPanel() {
+        return (
+            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
+                <DslProperties
+                    integration={this.props.integration}
+                    step={this.state.selectedStep}
+                    onIntegrationUpdate={this.onIntegrationUpdate}
+                    onPropertyUpdate={this.onPropertyUpdate}
+                    clipboardStep={undefined}
+                    isRouteDesigner={false}
+                    onClone={this.cloneRest}
+                    dark={this.props.dark}/>
+            </DrawerPanelContent>
+        )
+    }
+
+    render() {
+        const data = this.props.integration.spec.flows?.filter(f => f.dslName === 'RestDefinition');
+        const configData = this.props.integration.spec.flows?.filter(f => f.dslName === 'RestConfigurationDefinition');
+        const config = configData && Array.isArray(configData) ? configData[0] : undefined;
+        return (
+            <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
+                <div className="rest-page-columns">
+                    <Drawer isExpanded isInline>
+                        <DrawerContent panelContent={this.getPropertiesPanel()}>
+                            <DrawerContentBody>
+                                <div className="graph" data-click="REST" onClick={event => this.unselectElement(event)}>
+                                    <div className="flows">
+                                        {config && this.getRestConfigurationCard(config)}
+                                        {data && this.getRestCards(data)}
+                                        <div className="add-rest">
+                                            {config === undefined &&
+                                                <Button
+                                                    variant="primary"
+                                                    data-click="ADD_REST_REST_CONFIG"
+                                                    icon={<PlusIcon/>}
+                                                    onClick={e => this.createRestConfiguration()}>Create REST Configuration
+                                                </Button>
+                                            }
+                                            <Button
+                                                variant={data?.length === 0 ? "primary" : "secondary"}
+                                                data-click="ADD_REST"
+                                                icon={<PlusIcon/>}
+                                                onClick={e => this.createRest()}>Create REST Service
+                                            </Button>
+                                        </div>
+                                    </div>
+                                </div>
+                            </DrawerContentBody>
+                        </DrawerContent>
+                    </Drawer>
+                </div>
+                {this.getSelectorModal()}
+                {this.getDeleteConfirmation()}
+            </PageSection>
+        )
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/rest/RestMethodCard.tsx b/karavan-app/src/main/webui/src/designer/rest/RestMethodCard.tsx
new file mode 100644
index 0000000..b1a5cdc
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/rest/RestMethodCard.tsx
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {Button} from '@patternfly/react-core';
+import '../karavan.css';
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+
+interface Props<T> {
+    method: T
+    selectedStep?: CamelElement
+    integration: Integration
+    selectElement: (element: CamelElement) => void
+    deleteElement: (element: CamelElement) => void
+}
+
+interface State<T> {
+    method: T
+    expanded: boolean
+}
+
+export class RestMethodCard extends React.Component<Props<any>, State<any>> {
+
+    public state: State<any> = {
+        method: this.props.method,
+        expanded: false
+    };
+
+    selectElement = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.selectElement.call(this, this.state.method);
+    }
+
+    delete = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.deleteElement.call(this, this.props.method);
+    }
+
+    render() {
+        const method = this.state.method;
+        return (
+            <div className={this.props.selectedStep?.uuid === method.uuid ? "method-card method-card-selected" : "method-card method-card-unselected"} onClick={e => this.selectElement(e)}>
+                <div className="method">{method.dslName.replace('Definition', '').toUpperCase()}</div>
+                <div className="rest-method-desc">
+                    <div className="title">{method.path}</div>
+                    <div className="description">{method.description}</div>
+                </div>
+                <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
+            </div>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/rest/RestMethodSelector.tsx b/karavan-app/src/main/webui/src/designer/rest/RestMethodSelector.tsx
new file mode 100644
index 0000000..316145c
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/rest/RestMethodSelector.tsx
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Badge,
+    Card, CardBody, CardFooter, CardHeader, Gallery, PageSection,
+    Tab, Tabs, TabTitleText,
+    Text
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+
+interface Props {
+    onMethodSelect: (method: DslMetaModel) => void
+    dark: boolean
+}
+
+interface State {
+}
+
+export class RestMethodSelector extends React.Component<Props, State> {
+
+    public state: State = {};
+
+
+    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
+        this.setState({tabIndex: eventKey})
+    }
+
+
+    selectMethod = (evt: React.MouseEvent, method: any) => {
+        evt.stopPropagation()
+        this.props.onMethodSelect.call(this, method);
+    }
+
+    getCard(dsl: DslMetaModel, index: number) {
+        return (
+            <Card key={dsl.dsl + index} isHoverable isCompact className="dsl-card"
+                  onClick={event => this.selectMethod(event, dsl)}>
+                <CardHeader>
+                    {CamelUi.getIconForDsl(dsl)}
+                    <Text>{dsl.title}</Text>
+                </CardHeader>
+                <CardBody>
+                    <Text>{dsl.description}</Text>
+                </CardBody>
+                <CardFooter>
+                    {dsl.navigation.toLowerCase() === "kamelet"
+                        && <div className="footer" style={{justifyContent: "space-between"}}>
+                            <Badge isRead className="labels">{dsl.labels}</Badge>
+                            <Badge isRead className="version">{dsl.version}</Badge>
+                        </div>}
+                    {dsl.navigation.toLowerCase() === "component"
+                        && <div className="footer" style={{justifyContent: "flex-start"}}>
+                            {dsl.labels.split(',').map((s: string) => <Badge key={s} isRead className="labels">{s}</Badge>)}
+                        </div>}
+                </CardFooter>
+            </Card>
+        )
+    }
+
+    render() {
+        return (
+            <PageSection variant={this.props.dark ? "darker" : "light"}>
+                <Tabs style={{overflow: 'hidden'}} activeKey="methods" onSelect={this.selectTab}>
+                        <Tab eventKey="methods" title={<TabTitleText>Methods</TabTitleText>}>
+                            <Gallery hasGutter className="dsl-gallery">
+                                {CamelUi.getSelectorRestMethodModels().map((dsl: DslMetaModel, index: number) => this.getCard(dsl, index))}
+                            </Gallery>
+                        </Tab>
+                </Tabs>
+            </PageSection>
+        );
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx b/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
new file mode 100644
index 0000000..0806761
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
@@ -0,0 +1,434 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import '../karavan.css';
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {DslPosition, EventBus} from "../utils/EventBus";
+import {CamelUi} from "../utils/CamelUi";
+import {Subscription} from "rxjs";
+import {SagaDefinition} from "karavan-core/lib/model/CamelDefinition";
+
+interface Props {
+    integration: Integration
+    width: number
+    height: number
+    top: number
+    left: number
+}
+
+interface State {
+    integration: Integration
+    sub?: Subscription
+    steps: Map<string, DslPosition>
+}
+
+const overlapGap: number = 40;
+const outgoingDefinitions: string[] = ['ToDefinition', 'KameletDefinition', 'ToDynamicDefinition', "PollEnrichDefinition", "EnrichDefinition", "WireTapDefinition", "SagaDefinition"];
+
+
+export class DslConnections extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: this.props.integration,
+        steps: new Map<string, DslPosition>(),
+    };
+
+    componentDidMount() {
+        const sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => this.setPosition(evt));
+        this.setState({sub: sub});
+    }
+
+    componentWillUnmount() {
+        this.state.sub?.unsubscribe();
+    }
+
+    setPosition(evt: DslPosition) {
+        if (evt.command === "add") this.setState(prevState => ({steps: prevState.steps.set(evt.step.uuid, evt)}));
+        else if (evt.command === "delete") this.setState(prevState => {
+            // prevState.steps.clear();
+            prevState.steps.delete(evt.step.uuid);
+            return {steps: prevState.steps};
+        });
+    }
+
+    getIncomings() {
+        let outs: [string, number][] = Array.from(this.state.steps.values())
+            .filter(pos => ["FromDefinition"].includes(pos.step.dslName))
+            .filter(pos => !(pos.step.dslName === 'FromDefinition' && CamelUi.hasInternalUri(pos.step)))
+            .sort((pos1: DslPosition, pos2: DslPosition) => {
+                const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
+                const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
+                return y1 > y2 ? 1 : -1
+            })
+            .map(pos => [pos.step.uuid, pos.headerRect.y]);
+        while (this.hasOverlap(outs)) {
+            outs = this.addGap(outs);
+        }
+        return outs;
+    }
+
+    getIncoming(data: [string, number]) {
+        const pos = this.state.steps.get(data[0]);
+        if (pos) {
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const r = pos.headerRect.height / 2;
+
+            const incomingX = 20;
+            const lineX1 = incomingX + r;
+            const lineY1 = fromY;
+            const lineX2 = fromX - r * 2 + 7;
+            const lineY2 = fromY;
+
+            const imageX = incomingX - r + 5;
+            const imageY = fromY - r + 5;
+            return (
+                <g key={pos.step.uuid + "-incoming"}>
+                    <circle cx={incomingX} cy={fromY} r={r} className="circle-incoming"/>
+                    <image x={imageX} y={imageY} href={CamelUi.getConnectionIcon(pos.step)} className="icon"/>
+                    {/*<text x={imageX - 5} y={imageY + 40} className="caption" textAnchor="start">{CamelUi.getTitle(pos.step)}</text>*/}
+                    <path d={`M ${lineX1},${lineY1} C ${lineX1},${lineY2} ${lineX2},${lineY1}  ${lineX2},${lineY2}`}
+                          className="path-incoming" markerEnd="url(#arrowhead)"/>
+                </g>
+            )
+        }
+    }
+
+    hasOverlap(data: [string, number][]): boolean {
+        let result = false;
+        data.forEach((d, i, arr) => {
+            if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result = true;
+        })
+        return result;
+    }
+
+    addGap(data: [string, number][]): [string, number][] {
+        const result: [string, number][] = [];
+        data.forEach((d, i, arr) => {
+            if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result.push([d[0], d[1] + overlapGap])
+            else result.push(d);
+        })
+        return result;
+    }
+
+
+    getOutgoings(): [string, number][] {
+        let outs: [string, number][] = Array.from(this.state.steps.values())
+            .filter(pos => outgoingDefinitions.includes(pos.step.dslName))
+            .filter(pos => pos.step.dslName !== 'KameletDefinition' || (pos.step.dslName === 'KameletDefinition' && !CamelUi.isActionKamelet(pos.step)))
+            .filter(pos => pos.step.dslName === 'ToDefinition' && !CamelUi.isActionKamelet(pos.step))
+            .filter(pos => !(outgoingDefinitions.includes(pos.step.dslName) && CamelUi.hasInternalUri(pos.step)))
+            .filter(pos => pos.step.dslName !== 'SagaDefinition')
+            .sort((pos1: DslPosition, pos2: DslPosition) => {
+                const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
+                const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
+                return y1 > y2 ? 1 : -1
+            })
+            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top]);
+        while (this.hasOverlap(outs)) {
+            outs = this.addGap(outs);
+        }
+        // console.log(outs);
+        return outs;
+    }
+
+    getOutgoing(data: [string, number]) {
+        const pos = this.state.steps.get(data[0]);
+        if (pos) {
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const r = pos.headerRect.height / 2;
+
+            const outgoingX = this.props.width - 20;
+            const outgoingY = data[1] + 15;
+
+            const lineX1 = fromX + r;
+            const lineY1 = fromY;
+            const lineX2 = outgoingX - r * 2 + 4;
+            const lineY2 = outgoingY;
+
+            const lineXi = lineX1 + 40;
+            const lineYi = lineY2;
+
+            let image = CamelUi.getConnectionIcon(pos.step);
+            const imageX = outgoingX - r + 5;
+            const imageY = outgoingY - r + 5;
+            return (
+                <g key={pos.step.uuid + "-outgoing"}>
+                    <circle cx={outgoingX} cy={outgoingY} r={r} className="circle-outgoing"/>
+                    <image x={imageX} y={imageY} href={image} className="icon"/>
+                    {/*<text x={imageX + 25} y={imageY + 40}  className="caption" textAnchor="end">{CamelUi.getOutgoingTitle(pos.step)}</text>*/}
+                    <path d={`M ${lineX1},${lineY1} C ${lineXi - 20}, ${lineY1} ${lineX1 - 15},${lineYi} ${lineXi},${lineYi} L ${lineX2},${lineY2}`}
+                          className="path-incoming" markerEnd="url(#arrowhead)"/>
+                </g>
+            )
+        }
+    }
+
+    getInternals(): [string, number, boolean][] {
+        let outs: [string, number, boolean][] = Array.from(this.state.steps.values())
+            .filter(pos => outgoingDefinitions.includes(pos.step.dslName) && CamelUi.hasInternalUri(pos.step))
+            .sort((pos1: DslPosition, pos2: DslPosition) => {
+                const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
+                const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
+                return y1 > y2 ? 1 : -1
+            })
+            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top, pos.isSelected]);
+        return outs;
+    }
+
+    getInternalLines(data: [string, number, boolean]) {
+        const pos = this.state.steps.get(data[0]);
+        const uri = (pos?.step as any).uri;
+        if (uri && uri.length && pos) {
+            const key = pos.step.uuid + "-outgoing"
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const r = pos.headerRect.height / 2;
+            const className = (CamelUi.hasDirectUri(pos.step) ? "path-direct" : "path-seda") + (data[2] ? "-selected" : "");
+            return this.getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
+        } else if (pos?.step.dslName === 'SagaDefinition'){
+            const saga = (pos?.step as SagaDefinition);
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const r = pos.headerRect.height / 2;
+            const result:any[] = [];
+            if (saga.completion && (saga.completion.startsWith("direct") || saga.completion.startsWith("seda"))){
+                const key = pos.step.uuid + "-completion"
+                const className = saga.completion.startsWith("direct") ? "path-direct" : "path-seda";
+                result.push(this.getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
+            }
+            if (saga.compensation && (saga.compensation.startsWith("direct") || saga.compensation.startsWith("seda"))){
+                const key = pos.step.uuid + "-compensation"
+                const className = saga.compensation.startsWith("direct") ? "path-direct" : "path-seda";
+                result.push(this.getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
+            }
+            return result;
+        }
+    }
+
+    getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
+        const target = Array.from(this.state.steps.values())
+            .filter(s => s.step.dslName === 'FromDefinition')
+            .filter(s => (s.step as any).uri && (s.step as any).uri === uri)[0];
+        if (target) {
+            const targetX = target.headerRect.x + target.headerRect.width / 2 - this.props.left;
+            const targetY = target.headerRect.y + target.headerRect.height / 2 - this.props.top;
+            const gap = 100;
+            const add = 0.2;
+
+            // right
+            if (targetX - fromX >= gap) {
+                const startX = fromX + r;
+                const startY = fromY;
+                const endX = targetX - r * 2 + 4;
+                const endY = targetY;
+
+                const coefX = 24 + (i * add);
+                const coefY = (targetY > fromY) ? 24 : -24;
+
+                const pointX1 = startX + coefX;
+                const pointY1 = startY;
+                const pointX2 = startX + coefX;
+                const pointY2 = startY + coefY;
+
+                const pointLX = pointX1;
+                const pointLY = targetY - coefY;
+
+                const pointX3 = pointLX;
+                const pointY3 = endY;
+                const pointX4 = pointLX + coefX;
+                const pointY4 = endY;
+
+                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+            } else if (targetX > fromX && targetX - fromX < gap) {
+                const startX = fromX - r;
+                const startY = fromY;
+                const endX = targetX - r * 2 + 4;
+                const endY = targetY;
+
+                const coefX = -24 - (i * add);
+                const coefY = (targetY > fromY) ? 24 : -24;
+
+                const pointX1 = startX + coefX;
+                const pointY1 = startY;
+                const pointX2 = startX + coefX;
+                const pointY2 = startY + coefY;
+
+                const pointLX = pointX1;
+                const pointLY = targetY - coefY;
+
+                const pointX3 = pointLX;
+                const pointY3 = endY;
+                const pointX4 = pointLX - coefX/2;
+                const pointY4 = endY;
+
+                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+            } else if (targetX <= fromX && fromX - targetX < gap) {
+                const startX = fromX + r;
+                const startY = fromY;
+                const endX = targetX + r * 2 - 4;
+                const endY = targetY;
+
+                const coefX = 24 + (i * add);
+                const coefY = (targetY > fromY) ? 24 : -24;
+
+                const pointX1 = startX + coefX;
+                const pointY1 = startY;
+                const pointX2 = startX + coefX;
+                const pointY2 = startY + coefY;
+
+                const pointLX = pointX1;
+                const pointLY = targetY - coefY;
+
+                const pointX3 = pointLX;
+                const pointY3 = endY;
+                const pointX4 = pointLX - coefX/2;
+                const pointY4 = endY;
+
+                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+            } else {
+                const startX = fromX - r;
+                const startY = fromY;
+                const endX = targetX + r * 2 - 4;
+                const endY = targetY;
+
+                const coefX = -24 - (i * add);
+                const coefY = (targetY > fromY) ? 24 : -24;
+
+                const pointX1 = startX + coefX;
+                const pointY1 = startY;
+                const pointX2 = startX + coefX;
+                const pointY2 = startY + coefY;
+
+                const pointLX = pointX1;
+                const pointLY = targetY - coefY;
+
+                const pointX3 = pointLX;
+                const pointY3 = endY;
+                const pointX4 = pointLX + coefX;
+                const pointY4 = endY;
+
+                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+            }
+        }
+    }
+
+    getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
+                    pointX3: number, pointY3: number, pointX4: number, pointY4: number, endX: number, endY: number) {
+        return (
+            <g key={key}>
+                <path d={`M ${startX} ${startY} 
+                        Q ${pointX1} ${pointY1} ${pointX2} ${pointY2} L ${pointLX},${pointLY}
+                        Q ${pointX3} ${pointY3} ${pointX4} ${pointY4} L ${endX},${endY}`}
+                      className={className} markerEnd="url(#arrowhead)"/>
+            </g>
+        )
+    }
+
+    getCircle(pos: DslPosition) {
+        const cx = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
+        const cy = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+        const r = pos.headerRect.height / 2;
+        return (
+            <circle cx={cx} cy={cy} r={r} stroke="transparent" strokeWidth="3" fill="transparent" key={pos.step.uuid + "-circle"}/>
+        )
+    }
+
+    hasSteps = (step: CamelElement): boolean => {
+        return (step.hasSteps() && !['FromDefinition'].includes(step.dslName))
+            || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(step.dslName);
+    }
+
+    getPreviousStep(pos: DslPosition) {
+        return Array.from(this.state.steps.values())
+            .filter(p => pos.parent?.uuid === p.parent?.uuid)
+            .filter(p => p.inSteps)
+            .filter(p => p.position === pos.position - 1)[0];
+    }
+
+    getArrow(pos: DslPosition) {
+        const endX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
+        const endY = pos.headerRect.y - 9 - this.props.top;
+        if (pos.parent) {
+            const parent = this.state.steps.get(pos.parent.uuid);
+            if (parent) {
+                const startX = parent.headerRect.x + parent.headerRect.width / 2 - this.props.left;
+                const startY = parent.headerRect.y + parent.headerRect.height - this.props.top;
+                if ((!pos.inSteps || (pos.inSteps && pos.position === 0)) && parent.step.dslName !== 'MulticastDefinition') {
+                    return (
+                        <path name={pos.step.dslName} d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
+                              className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
+                    )
+                } else if (parent.step.dslName === 'MulticastDefinition' && pos.inSteps) {
+                    return (
+                        <path d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
+                              className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
+                    )
+                } else if (pos.inSteps && pos.position > 0 && !this.hasSteps(pos.step)) {
+                    const prev = this.getPreviousStep(pos);
+                    if (prev) {
+                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - this.props.left;
+                        const prevY = r.y + r.height - this.props.top;
+                        return (
+                            <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
+                        )
+                    }
+                } else if (pos.inSteps && pos.position > 0 && this.hasSteps(pos.step)) {
+                    const prev = this.getPreviousStep(pos);
+                    if (prev) {
+                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - this.props.left;
+                        const prevY = r.y + r.height - this.props.top;
+                        return (
+                            <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    getSvg() {
+        const steps = Array.from(this.state.steps.values());
+        return (
+            <svg
+                style={{width: this.props.width, height: this.props.height, position: "absolute", left: 0, top: 0}}
+                viewBox={"0 0 " + this.props.width + " " + this.props.height}>
+                <defs>
+                    <marker id="arrowhead" markerWidth="9" markerHeight="6" refX="0" refY="3" orient="auto" className="arrow">
+                        <polygon points="0 0, 9 3, 0 6"/>
+                    </marker>
+                </defs>
+                {steps.map(pos => this.getCircle(pos))}
+                {steps.map(pos => this.getArrow(pos))}
+                {this.getIncomings().map(p => this.getIncoming(p))}
+                {this.getOutgoings().map(p => this.getOutgoing(p))}
+                {this.getInternals().map((p) => this.getInternalLines(p)).flat()}
+            </svg>
+        )
+    }
+
+    render() {
+        return (
+            <div className="connections" style={{width: this.props.width, height: this.props.height, marginTop: "8px"}}>
+                {this.getSvg()}
+            </div>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/route/DslElement.tsx b/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
new file mode 100644
index 0000000..31ee222
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
@@ -0,0 +1,486 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React, {CSSProperties} from 'react';
+import {
+    Button,
+    Flex,
+    Modal, ModalVariant,
+    Text, Tooltip,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import InsertIcon from "@patternfly/react-icons/dist/js/icons/arrow-alt-circle-right-icon";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUi} from "../utils/CamelUi";
+import {EventBus} from "../utils/EventBus";
+import {ChildElement, CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import ReactDOM from "react-dom";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+
+interface Props {
+    integration: Integration,
+    step: CamelElement,
+    parent: CamelElement | undefined,
+    deleteElement: any
+    selectElement: any
+    openSelector: (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean, position?: number | undefined) => void
+    moveElement: (source: string, target: string, asChild: boolean) => void
+    selectedUuid: string
+    inSteps: boolean
+    position: number
+}
+
+interface State {
+    showSelector: boolean
+    showMoveConfirmation: boolean
+    moveElements: [string | undefined, string | undefined]
+    tabIndex: string | number
+    selectedUuid: string
+    isDragging: boolean
+    isDraggedOver: boolean
+}
+
+export class DslElement extends React.Component<Props, State> {
+
+    public state: State = {
+        showSelector: false,
+        showMoveConfirmation: false,
+        moveElements: [undefined, undefined],
+        tabIndex: 0,
+        selectedUuid: this.props.selectedUuid,
+        isDragging: false,
+        isDraggedOver: false
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.selectedUuid !== this.props.selectedUuid) {
+            this.setState({selectedUuid: this.props.selectedUuid});
+        }
+    }
+
+    openSelector = (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) => {
+        evt.stopPropagation();
+        if (isInsert && this.props.parent) {
+            this.props.openSelector.call(this, this.props.parent.uuid, this.props.parent.dslName, showSteps, this.props.position);
+        } else {
+            this.props.openSelector.call(this, this.props.step.uuid, this.props.step.dslName, showSteps);
+        }
+    }
+
+    closeDslSelector = () => {
+        this.setState({showSelector: false})
+    }
+
+    delete = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.deleteElement.call(this, this.props.step.uuid);
+    }
+
+    selectElement = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.selectElement.call(this, this.props.step);
+    }
+
+    dragElement = (event: React.DragEvent<HTMLDivElement>, element: CamelElement) => {
+        event.preventDefault();
+        event.stopPropagation();
+        this.setState({isDraggedOver: false});
+        const sourceUuid = event.dataTransfer.getData("text/plain");
+        const targetUuid = element.uuid;
+        if (sourceUuid !== targetUuid) {
+            if (element.hasSteps()){
+                this.setState({showMoveConfirmation: true, moveElements: [sourceUuid, targetUuid]});
+            } else {
+                this.props.moveElement?.call(this, sourceUuid, targetUuid, false);
+            }
+        }
+    }
+
+    confirmMove = (asChild: boolean) => {
+        const sourceUuid = this.state.moveElements[0];
+        const targetUuid = this.state.moveElements[1];
+        if (sourceUuid && targetUuid && sourceUuid !== targetUuid) {
+            this.props.moveElement?.call(this, sourceUuid, targetUuid, asChild);
+            this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+        }
+    }
+
+    cancelMove = () => {
+        this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+    }
+
+    isSelected = (): boolean => {
+        return this.state.selectedUuid === this.props.step.uuid
+    }
+
+    hasBorder = (): boolean => {
+        return (this.props.step?.hasSteps() && !['FromDefinition'].includes(this.props.step.dslName))
+            || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(this.props.step.dslName);
+    }
+
+    isNotDraggable = (): boolean => {
+        return ['FromDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName);
+    }
+
+    isWide = (): boolean => {
+        return ['RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
+            .includes(this.props.step.dslName);
+    }
+
+    isAddStepButtonLeft = (): boolean => {
+        return ['MulticastDefinition']
+            .includes(this.props.step.dslName);
+    }
+
+    isHorizontal = (): boolean => {
+        return ['MulticastDefinition'].includes(this.props.step.dslName);
+    }
+
+    isRoot = (): boolean => {
+        return this.props.step?.dslName?.startsWith("RouteDefinition");
+    }
+
+    isInStepWithChildren = () => {
+        const step: CamelElement = this.props.step;
+        const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
+        return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && this.props.inSteps;
+    }
+
+    getChildrenInfo = (step: CamelElement): [boolean, number, boolean, number, number] => {
+        const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
+        const hasStepsField = children.filter((c: ChildElement) => c.name === 'steps').length === 1;
+        const stepsChildrenCount = children
+            .filter(c => c.name === 'steps')
+            .map((child: ChildElement, index: number) => {
+                const children: CamelElement[] = CamelDefinitionApiExt.getElementChildren(step, child);
+                return children.length;
+            }).reduce((a, b) => a + b, 0);
+
+        const hasNonStepsFields = children.filter(c => c.name !== 'steps' && c.name !== 'expression' && c.name !== 'onWhen').length > 0;
+        const childrenCount = children
+            .map((child: ChildElement, index: number) => {
+                const children: CamelElement[] = CamelDefinitionApiExt.getElementChildren(step, child);
+                return children.length;
+            }).reduce((a, b) => a + b, 0);
+        const nonStepChildrenCount = childrenCount - stepsChildrenCount;
+        return [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount]
+    }
+
+    hasWideChildrenElement = () => {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = this.getChildrenInfo(this.props.step);
+        if (this.isHorizontal() && stepsChildrenCount > 1) return true;
+        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
+        else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) return true;
+        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && childrenCount > 1) return true;
+        else return false;
+    }
+
+    hasBorderOverSteps = (step: CamelElement) => {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = this.getChildrenInfo(step);
+        if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
+        else return false;
+    }
+
+    getHeaderStyle = () => {
+        const style: CSSProperties = {
+            width: this.isWide() ? "100%" : "",
+            fontWeight: this.isSelected() ? "bold" : "normal",
+        };
+        return style;
+    }
+
+    sendPosition = (el: HTMLDivElement | null, isSelected: boolean) => {
+        const node = ReactDOM.findDOMNode(this);
+        if (node && el) {
+            const header = Array.from(node.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
+            if (header) {
+                const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
+                const headerRect = headerIcon.getBoundingClientRect();
+                const rect = el.getBoundingClientRect();
+                if (this.props.step.show){
+                    EventBus.sendPosition("add", this.props.step, this.props.parent, rect, headerRect, this.props.position, this.props.inSteps, isSelected);
+                } else {
+                    EventBus.sendPosition("delete", this.props.step, this.props.parent, new DOMRect(), new DOMRect(), 0);
+                }
+            }
+        }
+    }
+
+    getHeader = () => {
+        const step: CamelElement = this.props.step;
+        const availableModels = CamelUi.getSelectorModelsForParent(step.dslName, false);
+        const showAddButton = !['CatchDefinition', 'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
+        const showInsertButton = !['FromDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName);
+        const headerClass = step.dslName === 'RouteDefinition' ? "header-route" : "header"
+        const headerClasses = this.isSelected() ? headerClass + " selected" : headerClass;
+        return (
+            <div className={headerClasses} style={this.getHeaderStyle()}>
+                {this.props.step.dslName !== 'RouteDefinition' &&
+                    <div ref={el => this.sendPosition(el, this.isSelected())}
+                         className={"header-icon"}
+                         style={this.isWide() ? {width: ""} : {}}>
+                        {CamelUi.getIconForElement(step)}
+                    </div>
+                }
+                <div className={this.hasWideChildrenElement() ? "header-text" : ""}>
+                    {this.hasWideChildrenElement() && <div className="spacer"/>}
+                    {this.getHeaderTextWithTooltip(step)}
+                </div>
+                {showInsertButton && this.getInsertElementButton()}
+                {this.getDeleteButton()}
+                {showAddButton && this.getAddElementButton()}
+            </div>
+        )
+    }
+
+    getHeaderTextWithTooltip = (step: CamelElement) => {
+        const checkRequired = CamelUtil.checkRequired(step);
+        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(this.props.step);
+        let className = this.hasWideChildrenElement() ? "text text-right" : "text text-bottom";
+        if (!checkRequired[0]) className = className + " header-text-required";
+        if (checkRequired[0]) return <Text className={className}>{title}</Text>
+        else return (
+            <Tooltip position={"right"} className="tooltip-required-field"
+                     content={checkRequired[1].map((text, i) =>(<div key={i}>{text}</div>))}>
+                <Text className={className}>{title}</Text>
+            </Tooltip>
+        )
+    }
+
+    getHeaderWithTooltip = (tooltip: string | undefined) => {
+        return (
+            <Tooltip position={"left"}
+                     content={<div>{tooltip}</div>}>
+                {this.getHeader()}
+            </Tooltip>
+        )
+    }
+
+    getHeaderTooltip = (): string | undefined => {
+        if (CamelUi.isShowExpressionTooltip(this.props.step)) return CamelUi.getExpressionTooltip(this.props.step);
+        if (CamelUi.isShowUriTooltip(this.props.step)) return CamelUi.getUriTooltip(this.props.step);
+        return undefined;
+    }
+
+    getElementHeader = () => {
+        const tooltip = this.getHeaderTooltip();
+        if (tooltip !== undefined && !this.state.isDragging) {
+            return this.getHeaderWithTooltip(tooltip);
+        }
+        return this.getHeader();
+    }
+
+    getChildrenStyle = () => {
+        const style: CSSProperties = {
+            display: "flex",
+            flexDirection: "row",
+        }
+        return style;
+    }
+
+    getChildrenElementsStyle = (child: ChildElement, notOnlySteps: boolean) => {
+        const step = this.props.step;
+        const isBorder = child.name === 'steps' && this.hasBorderOverSteps(step);
+        const style: CSSProperties = {
+            borderStyle: isBorder ? "dotted" : "none",
+            borderColor: "var(--step-border-color)",
+            borderWidth: "1px",
+            borderRadius: "16px",
+            display: this.isHorizontal() || child.name !== 'steps' ? "flex" : "block",
+            flexDirection: "row",
+        }
+        return style;
+    }
+
+    getChildElements = () => {
+        const step: CamelElement = this.props.step;
+        let children: ChildElement[] = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
+        const notOnlySteps = children.filter(c => c.name === 'steps').length === 1
+            && children.filter(c => c.multiple && c.name !== 'steps').length > 0;
+
+        if (step.dslName !== 'RouteDefinition') {
+            children = children.filter(child => {
+                const cc = CamelDefinitionApiExt.getElementChildrenDefinition(child.className);
+                return child.name === 'steps' || cc.filter(c => c.multiple).length > 0;
+            })
+        }
+        if (step.dslName === 'CatchDefinition') { // exception
+            children = children.filter(value => value.name !== 'onWhen')
+        }
+        return (
+            <div key={step.uuid + "-children"} className="children" style={this.getChildrenStyle()}>
+                {children.map((child: ChildElement, index: number) => this.getChildDslElements(child, index, notOnlySteps))}
+            </div>
+        )
+    }
+
+    getChildDslElements = (child: ChildElement, index: number, notOnlySteps: boolean) => {
+        const step = this.props.step;
+        const children: CamelElement[] = CamelDefinitionApiExt.getElementChildren(step, child);
+        if (children.length > 0) {
+            return (
+                <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+                    {children.map((element, index) => (
+                        <div key={step.uuid + child.className + index}>
+                            <DslElement
+                                integration={this.props.integration}
+                                openSelector={this.props.openSelector}
+                                deleteElement={this.props.deleteElement}
+                                selectElement={this.props.selectElement}
+                                moveElement={this.props.moveElement}
+                                selectedUuid={this.state.selectedUuid}
+                                inSteps={child.name === 'steps'}
+                                position={index}
+                                step={element}
+                                parent={step}/>
+                        </div>
+                    ))}
+                    {child.name === 'steps' && this.getAddStepButton()}
+                </div>
+            )
+        } else if (child.name === 'steps') {
+            return (
+                <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+                    {this.getAddStepButton()}
+                </div>
+            )
+        }
+    }
+
+    getAddStepButton() {
+        const {integration, step, selectedUuid} = this.props;
+        const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuid);
+        if (hideAddButton) return (<></>)
+        else return (
+            <Tooltip position={"bottom"}
+                     content={<div>{"Add step to " + CamelUi.getTitle(step)}</div>}>
+                <button type="button" aria-label="Add" onClick={e => this.openSelector(e)}
+                        className={this.isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
+                    <AddIcon noVerticalAlign/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    getAddElementButton() {
+        return (
+            <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(this.props.step)}</div>}>
+                <button
+                    type="button"
+                    aria-label="Add"
+                    onClick={e => this.openSelector(e, false)}
+                    className={"add-element-button"}>
+                    <AddIcon noVerticalAlign/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    getInsertElementButton() {
+        return (
+            <Tooltip position={"left"} content={<div>{"Insert element before"}</div>}>
+                <button type="button" aria-label="Insert" onClick={e => this.openSelector(e, true, true)} className={"insert-element-button"}><InsertIcon noVerticalAlign/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    getDeleteButton() {
+        return (
+            <Tooltip position={"right"} content={<div>{"Delete element"}</div>}>
+                <button type="button" aria-label="Delete" onClick={e => this.delete(e)} className="delete-button"><DeleteIcon noVerticalAlign/></button>
+            </Tooltip>
+        )
+    }
+
+    getMoveConfirmation() {
+        return (
+            <Modal
+                aria-label="title"
+                className='move-modal'
+                isOpen={this.state.showMoveConfirmation}
+                variant={ModalVariant.small}
+            ><Flex direction={{default: "column"}}>
+                <div>Select move type:</div>
+                <Button key="place" variant="primary" onClick={event => this.confirmMove(false)}>Shift (target down)</Button>
+                <Button key="child" variant="secondary" onClick={event => this.confirmMove(true)}>Move as target step</Button>
+                <Button key="cancel" variant="tertiary" onClick={event => this.cancelMove()}>Cancel</Button>
+            </Flex>
+
+            </Modal>
+        )
+    }
+
+    render() {
+        const element: CamelElement = this.props.step;
+        const className = "step-element" + (this.isSelected() ? " step-element-selected" : "")
+            + (!this.props.step.show ? " hidden-step" : "");
+        return (
+            <div key={"root" + element.uuid}
+                 className={className}
+                 ref={el => this.sendPosition(el, this.isSelected())}
+                 style={{
+                     borderStyle: this.hasBorder() ? "dotted" : "none",
+                     borderColor: this.isSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
+                     marginTop: this.isInStepWithChildren() ? "16px" : "8px",
+                     zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
+                     boxShadow: this.state.isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
+                 }}
+                 onMouseOver={event => event.stopPropagation()}
+                 onClick={event => this.selectElement(event)}
+                 onDragStart={event => {
+                     event.stopPropagation();
+                     event.dataTransfer.setData("text/plain", element.uuid);
+                     (event.target as any).style.opacity = .5;
+                     this.setState({isDragging: true});
+                 }}
+                 onDragEnd={event => {
+                     (event.target as any).style.opacity = '';
+                     this.setState({isDragging: false});
+                 }}
+                 onDragOver={event => {
+                     event.preventDefault();
+                     event.stopPropagation();
+                     if (element.dslName !== 'FromDefinition' && !this.state.isDragging) {
+                         this.setState({isDraggedOver: true});
+                     }
+                 }}
+                 onDragEnter={event => {
+                     event.preventDefault();
+                     event.stopPropagation();
+                     if (element.dslName !== 'FromDefinition') {
+                         this.setState({isDraggedOver: true});
+                     }
+                 }}
+                 onDragLeave={event => {
+                     event.preventDefault();
+                     event.stopPropagation();
+                     this.setState({isDraggedOver: false});
+
+                 }}
+                 onDrop={event => this.dragElement(event, element)}
+                 draggable={!this.isNotDraggable()}
+            >
+                {this.getElementHeader()}
+                {this.getChildElements()}
+                {this.getMoveConfirmation()}
+            </div>
+        )
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx b/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
new file mode 100644
index 0000000..0359bb3
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Form,
+    Text,
+    Title,
+    TextVariants, ExpandableSection, Button, Tooltip,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import {DataFormatField} from "./property/DataFormatField";
+import {DslPropertyField} from "./property/DslPropertyField";
+import {
+    ExpressionDefinition,
+    DataFormatDefinition
+} from "karavan-core/lib/model/CamelDefinition";
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {CamelUi, RouteToCreate} from "../utils/CamelUi";
+import {CamelMetadataApi, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
+import {IntegrationHeader} from "../utils/KaravanComponents";
+import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon'
+import PasteIcon from '@patternfly/react-icons/dist/esm/icons/paste-icon'
+import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
+
+interface Props {
+    integration: Integration,
+    step?: CamelElement,
+    onIntegrationUpdate?: any,
+    onPropertyUpdate?: (element: CamelElement, newRoute?: RouteToCreate) => void
+    clipboardStep?: CamelElement
+    onSaveClipboardStep?: (element?: CamelElement) => void
+    onClone?: (element: CamelElement) => void
+    isRouteDesigner: boolean
+    dark: boolean
+}
+
+interface State {
+    step?: CamelElement,
+    selectStatus: Map<string, boolean>
+    isShowAdvanced: boolean
+    isDescriptionExpanded?: boolean
+}
+
+export class DslProperties extends React.Component<Props, State> {
+
+    public state: State = {
+        step: this.props.step,
+        selectStatus: new Map<string, boolean>(),
+        isShowAdvanced: false
+    };
+
+    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
+        if (this.state.step) {
+            const clone = CamelUtil.cloneStep(this.state.step);
+            (clone as any)[fieldId] = value;
+            this.setStep(clone)
+            this.props.onPropertyUpdate?.call(this, clone, newRoute);
+        }
+    }
+
+    pasteClipboardStep = () => {
+        if (this.props.clipboardStep && this.state.step) {
+            const clone = CamelUtil.cloneStep(this.props.clipboardStep);
+            clone.uuid = this.state.step.uuid;
+            this.setStep(clone)
+            this.props.onPropertyUpdate?.call(this, clone);
+        }
+    }
+
+    dataFormatChanged = (value: DataFormatDefinition) => {
+        value.uuid = this.state.step?.uuid ? this.state.step?.uuid : value.uuid;
+        this.setStep(value);
+        this.props.onPropertyUpdate?.call(this, value);
+    }
+
+    expressionChanged = (propertyName: string, exp: ExpressionDefinition) => {
+        if (this.state.step) {
+            const clone = (CamelUtil.cloneStep(this.state.step));
+            (clone as any)[propertyName] = exp;
+            this.setStep(clone);
+            this.props.onPropertyUpdate?.call(this, clone);
+        }
+    }
+
+    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
+        if (this.state.step && this.state.step) {
+            if (pathParameter) {
+                const uri = ComponentApi.buildComponentUri((this.state.step as any).uri, parameter, value);
+                this.propertyChanged("uri", uri, newRoute);
+            } else {
+                const clone = (CamelUtil.cloneStep(this.state.step));
+                const parameters: any = {...(clone as any).parameters};
+                parameters[parameter] = value;
+                (clone as any).parameters = parameters;
+                this.setStep(clone);
+                this.props.onPropertyUpdate?.call(this, clone);
+            }
+        }
+    }
+
+    cloneElement = () => {
+        if (this.state.step) {
+            this.props.onClone?.call(this, this.state.step);
+        }
+    }
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevProps.step !== this.props.step) {
+            this.setStep(this.props.step);
+        }
+    }
+
+    setStep = (step?: CamelElement) => {
+        this.setState({
+            step: step,
+            selectStatus: new Map<string, boolean>()
+        });
+    }
+
+    getRouteHeader= (): JSX.Element => {
+        const isDescriptionExpanded = this.state.isDescriptionExpanded;
+        const title = this.state.step && CamelUi.getTitle(this.state.step)
+        const description =  this.state.step &&  CamelUi.getDescription(this.state.step);
+        const descriptionLines: string [] = description ? description?.split("\n") : [""];
+        return (
+            <div className="headers">
+                <div className="top">
+                    <Title headingLevel="h1" size="md">{title}</Title>
+                    <Tooltip content="Copy step" position="bottom">
+                        <Button variant="link" onClick={() => this.props.onSaveClipboardStep?.call(this, this.state.step)} icon={<CopyIcon/>}/>
+                    </Tooltip>
+                    <Tooltip content="Paste step" position="bottom">
+                        <Button variant="link" onClick={() => this.pasteClipboardStep()} icon={<PasteIcon/>}/>
+                    </Tooltip>
+                </div>
+                <Text component={TextVariants.p}>{descriptionLines.at(0)}</Text>
+                {descriptionLines.length > 1 && <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
+                                                                   onToggle={isExpanded => this.setState({isDescriptionExpanded: !isDescriptionExpanded})}
+                                                                   isExpanded={isDescriptionExpanded}>
+                    {descriptionLines.filter((value, index) => index > 0)
+                        .map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+                </ExpandableSection>}
+            </div>
+        )
+    }
+
+    getClonableElementHeader = (): JSX.Element => {
+        const title = this.state.step && CamelUi.getTitle(this.state.step);
+        const description = this.state.step?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(this.state.step?.dslName)?.description : title;
+        const descriptionLines: string [] = description ? description?.split("\n") : [""];
+        return (
+            <div className="headers">
+                <div className="top">
+                    <Title headingLevel="h1" size="md">{title}</Title>
+                    <Tooltip content="Clone element" position="bottom">
+                        <Button variant="link" onClick={() => this.cloneElement()} icon={<CloneIcon/>}/>
+                    </Tooltip>
+                </div>
+                {descriptionLines.map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+            </div>
+        )
+    }
+
+    getComponentHeader = (): JSX.Element => {
+        if (this.props.isRouteDesigner) return this.getRouteHeader()
+        else return this.getClonableElementHeader();
+    }
+
+    getProperties = (): PropertyMeta[] => {
+        const dslName = this.state.step?.dslName;
+        return CamelDefinitionApiExt.getElementProperties(dslName)
+            // .filter((p: PropertyMeta) => (showAdvanced && p.label.includes('advanced')) || (!showAdvanced && !p.label.includes('advanced')))
+            .filter((p: PropertyMeta) => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'))
+            .filter((p: PropertyMeta) => !(dslName === 'RestDefinition' && ['get', 'post', 'put', 'patch', 'delete', 'head'].includes(p.name)));
+        // .filter((p: PropertyMeta) => dslName && !(['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(dslName) && ['param', 'responseMessage'].includes(p.name))) // TODO: configure this properties
+    }
+
+    getPropertyFields = (properties: PropertyMeta[]) => {
+        return (<>
+            {this.state.step && !['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName) && properties.map((property: PropertyMeta) =>
+                <DslPropertyField key={property.name}
+                                  integration={this.props.integration}
+                                  property={property}
+                                  element={this.state.step}
+                                  value={this.state.step ? (this.state.step as any)[property.name] : undefined}
+                                  onExpressionChange={this.expressionChanged}
+                                  onParameterChange={this.parametersChanged}
+                                  onDataFormatChange={this.dataFormatChanged}
+                                  onChange={this.propertyChanged}
+                                  dark={this.props.dark}/>
+            )}
+        </>)
+    }
+
+    render() {
+        const properties = this.getProperties();
+        const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
+        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+        return (
+            <div key={this.state.step ? this.state.step.uuid : 'integration'}
+                 className='properties'>
+                <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
+                    {this.state.step === undefined && <IntegrationHeader integration={this.props.integration}/>}
+                    {this.state.step && this.getComponentHeader()}
+                    {this.getPropertyFields(propertiesMain)}
+                    {propertiesAdvanced.length > 0 &&
+                        <ExpandableSection
+                            toggleText={'Advanced properties'}
+                            onToggle={isExpanded => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
+                            isExpanded={this.state.isShowAdvanced}>
+                            <div className="parameters">
+                                {this.getPropertyFields(propertiesAdvanced)}
+                            </div>
+                        </ExpandableSection>}
+                    {this.state.step && ['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName) &&
+                        <DataFormatField
+                            integration={this.props.integration}
+                            dslName={this.state.step.dslName}
+                            value={this.state.step}
+                            onDataFormatChange={this.dataFormatChanged}
+                            dark={this.props.dark}/>
+                    }
+                </Form>
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx b/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
new file mode 100644
index 0000000..c36d76a
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Badge,
+    Card, CardBody, CardFooter, CardHeader, Flex, FlexItem, Form, FormGroup, Gallery, Modal, PageSection,
+    Tab, Tabs, TabTitleText,
+    Text, TextInput,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+
+interface Props {
+    onDslSelect: (dsl: DslMetaModel, parentId: string, position?: number | undefined) => void,
+    onClose?: () => void,
+    parentId: string,
+    parentDsl?: string,
+    showSteps: boolean,
+    dark: boolean,
+    isOpen: boolean,
+    position?: number
+    tabIndex?: string | number
+}
+
+interface State {
+    tabIndex: string | number
+    filter: string;
+}
+
+export class DslSelector extends React.Component<Props, State> {
+
+    getDefaultTabIndex = () => {
+        const x = CamelUi.getSelectorModelTypes(this.props.parentDsl, this.props.showSteps);
+        if (x.length > 0) return x[0][0]
+        else return '';
+    }
+
+    public state: State = {
+        tabIndex: this.props.tabIndex ? this.props.tabIndex : this.getDefaultTabIndex(),
+        filter: ''
+    }
+
+    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
+        this.setState({tabIndex: eventKey})
+    }
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevProps.parentDsl !== this.props.parentDsl) {
+            this.setState({tabIndex: CamelUi.getSelectorModelTypes(this.props.parentDsl, this.props.showSteps)[0][0]});
+        }
+    }
+
+    selectDsl = (evt: React.MouseEvent, dsl: any) => {
+        evt.stopPropagation();
+        this.setState({filter:""});
+        this.props.onDslSelect.call(this, dsl, this.props.parentId, this.props.position);
+    }
+
+    searchInput = () => {
+        return (
+            <Form isHorizontal className="search" autoComplete="off">
+                <FormGroup fieldId="search">
+                    <TextInput className="text-field" type="text" id="search" name="search" iconVariant='search'
+                               value={this.state.filter}
+                               onChange={e => this.setState({filter: e})}/>
+                </FormGroup>
+            </Form>
+        )
+    }
+
+    getCard(dsl: DslMetaModel, index: number) {
+        return (
+            <Card key={dsl.dsl + index} isHoverable isCompact className="dsl-card"
+                  onClick={event => this.selectDsl(event, dsl)}>
+                <CardHeader>
+                    {CamelUi.getIconForDsl(dsl)}
+                    <Text>{dsl.title}</Text>
+                    {dsl.supportLevel && dsl.supportLevel === 'Preview' && <Badge isRead className="support-level">{dsl.supportLevel}</Badge>}
+                </CardHeader>
+                <CardBody>
+                    <Text>{dsl.description}</Text>
+                </CardBody>
+                <CardFooter>
+                    {dsl.navigation.toLowerCase() === "kamelet"
+                        && <div className="footer" style={{justifyContent: "space-between"}}>
+                            <Badge isRead className="labels">{dsl.labels}</Badge>
+                            <Badge isRead className="version">{dsl.version}</Badge>
+                        </div>}
+                    {dsl.navigation.toLowerCase() === "component"
+                        && <div className="footer" style={{justifyContent: "flex-start"}}>
+                            {dsl.labels.split(',').map((s: string,  i: number) => <Badge key={s + i} isRead
+                                                                                         className="labels">{s}</Badge>)}
+                            <Badge isRead className="version">{dsl.version}</Badge>
+                        </div>
+                    }
+                </CardFooter>
+            </Card>
+        )
+    }
+
+    close = () => {
+        this.setState({filter:""});
+        this.props.onClose?.call(this);
+    }
+
+    render() {
+        const parentDsl = this.props.parentDsl;
+        const title = parentDsl === undefined ? "Select source/from" : "Select step";
+        const labelText: string = this.state.tabIndex ? this.state.tabIndex.toString() : "";
+        return (
+            <Modal
+                aria-label={title}
+                width={'90%'}
+                className='dsl-modal'
+                isOpen={this.props.isOpen}
+                onClose={() => this.close()}
+                header={
+                    <Flex direction={{default: "column"}}>
+                        <FlexItem>
+                            <h3>{title}</h3>
+                            {this.searchInput()}
+                        </FlexItem>
+                        <FlexItem>
+                            <Tabs style={{overflow: 'hidden'}} activeKey={this.state.tabIndex}
+                                  onSelect={this.selectTab}>
+                                {CamelUi.getSelectorModelTypes(parentDsl, this.props.showSteps,this.state.filter).map((label: [string, number], index: number) => {
+                                    const labelText = label[0];
+                                    const count = label[1];
+                                    const title = ['kamelet', 'component'].includes(labelText.toLowerCase()) ? labelText + "s (" + count + ")" : labelText;
+                                    return (
+                                        <Tab eventKey={labelText} key={"tab-" + labelText}
+                                             title={<TabTitleText>{CamelUtil.capitalizeName(title)}</TabTitleText>}>
+                                        </Tab>
+                                    )
+                                })}
+                            </Tabs>
+                        </FlexItem>
+                    </Flex>
+                }
+                actions={{}}>
+                <PageSection variant={this.props.dark ? "darker" : "light"}>
+                    <Gallery key={"gallery-" + labelText} hasGutter className="dsl-gallery">
+                        {CamelUi.getSelectorModelsForParentFiltered(parentDsl, labelText, this.props.showSteps)
+                            .filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, this.state.filter))
+                            .map((dsl: DslMetaModel, index: number) => this.getCard(dsl, index))}
+                    </Gallery>
+                </PageSection>
+            </Modal>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
new file mode 100644
index 0000000..14ded06
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
@@ -0,0 +1,405 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Drawer,
+    DrawerPanelContent,
+    DrawerContent,
+    DrawerContentBody,
+    Button, Modal,
+    PageSection,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {DslSelector} from "./DslSelector";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {DslProperties} from "./DslProperties";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {FromDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {DslConnections} from "./DslConnections";
+import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {DslElement} from "./DslElement";
+import {EventBus} from "../utils/EventBus";
+import {CamelUi, RouteToCreate} from "../utils/CamelUi";
+import {findDOMNode} from "react-dom";
+import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {toPng} from 'html-to-image';
+
+interface Props {
+    onSave?: (integration: Integration, propertyOnly: boolean) => void
+    integration: Integration
+    dark: boolean
+}
+
+interface State {
+    integration: Integration
+    selectedStep?: CamelElement
+    showSelector: boolean
+    showDeleteConfirmation: boolean
+    deleteMessage: string
+    parentId: string
+    parentDsl?: string
+    selectedPosition?: number
+    showSteps: boolean
+    selectedUuid: string
+    key: string
+    width: number
+    height: number
+    top: number
+    left: number
+    clipboardStep?: CamelElement
+    ref?: any
+    printerRef?: any
+    propertyOnly: boolean
+    selectorTabIndex?: string | number
+}
+
+export class RouteDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: CamelDisplayUtil.setIntegrationVisibility(this.props.integration, undefined),
+        showSelector: false,
+        showDeleteConfirmation: false,
+        deleteMessage: '',
+        parentId: '',
+        showSteps: true,
+        selectedUuid: '',
+        key: "",
+        width: 1000,
+        height: 1000,
+        top: 0,
+        left: 0,
+        ref: React.createRef(),
+        printerRef: React.createRef(),
+        propertyOnly: false,
+    };
+
+    componentDidMount() {
+        window.addEventListener('resize', this.handleResize);
+        const element = findDOMNode(this.state.ref.current)?.parentElement?.parentElement;
+        const checkResize = (mutations: any) => {
+            const el = mutations[0].target;
+            const w = el.clientWidth;
+            const isChange = mutations.map((m: any) => `${m.oldValue}`).some((prev: any) => prev.indexOf(`width: ${w}px`) === -1);
+            if (isChange) this.setState({key: Math.random().toString()});
+        }
+        if (element) {
+            const observer = new MutationObserver(checkResize);
+            observer.observe(element, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
+        }
+    }
+
+    componentWillUnmount() {
+        window.removeEventListener('resize', this.handleResize);
+    }
+
+    handleResize = (event: any) => {
+        this.setState({key: Math.random().toString()});
+    }
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.key !== this.state.key) {
+            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
+        }
+    }
+
+    saveToClipboard = (step?: CamelElement): void => {
+        this.setState({clipboardStep: step, key: Math.random().toString()});
+    }
+
+    onPropertyUpdate = (element: CamelElement, newRoute?: RouteToCreate) => {
+        if (newRoute) {
+            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.state.integration, element);
+            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName + ":" + newRoute.name})
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            this.setState({
+                integration: clone,
+                key: Math.random().toString(),
+                showSelector: false,
+                selectedStep: element,
+                selectedUuid: element.uuid,
+                propertyOnly: false
+            });
+        } else {
+            const clone = CamelUtil.cloneIntegration(this.state.integration);
+            const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
+            this.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
+        }
+    }
+
+    showDeleteConfirmation = (id: string) => {
+        let message: string;
+        let ce: CamelElement;
+        ce = CamelDefinitionApiExt.findElementInIntegration(this.state.integration, id)!;
+        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.  Use its uuid.
+            let flows = this.state.integration.spec.flows!;
+            for (let i = 0; i < flows.length; i++) {
+                if (flows[i].dslName === 'RouteDefinition') {
+                    let routeDefinition: RouteDefinition = flows[i];
+                    if (routeDefinition.from.uuid === id) {
+                        id = routeDefinition.uuid;
+                        break;
+                    }
+                }
+            }
+            message = 'Deleting the first element will delete the entire route!';
+        } else if (ce.dslName === 'RouteDefinition') {
+            message = 'Delete route?';
+        } else {
+            message = 'Delete element from route?';
+        }
+        this.setState({selectedUuid: id, showSelector: false, showDeleteConfirmation: true, deleteMessage: message});
+    }
+
+    deleteElement = () => {
+        const id = this.state.selectedUuid;
+        const i = CamelDefinitionApiExt.deleteStepFromIntegration(this.state.integration, id);
+        this.setState({
+            integration: i,
+            showSelector: false,
+            showDeleteConfirmation: false,
+            deleteMessage: '',
+            key: Math.random().toString(),
+            selectedStep: undefined,
+            selectedUuid: '',
+            propertyOnly: false
+        });
+        const el = new CamelElement("");
+        el.uuid = id;
+        EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
+    }
+
+    selectElement = (element: CamelElement) => {
+        const i = CamelDisplayUtil.setIntegrationVisibility(this.state.integration, element.uuid);
+        this.setState({integration: i, selectedStep: element, selectedUuid: element.uuid, showSelector: false})
+    }
+
+    unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+        if ((evt.target as any).dataset.click === 'FLOWS') {
+            evt.stopPropagation()
+            const i = CamelDisplayUtil.setIntegrationVisibility(this.state.integration, undefined);
+            this.setState({integration: i, selectedStep: undefined, selectedUuid: '', showSelector: false, selectedPosition: undefined})
+        }
+    }
+
+    openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
+        this.setState({
+            showSelector: true,
+            parentId: parentId || '',
+            parentDsl: parentDsl,
+            showSteps: showSteps,
+            selectedPosition: position,
+            selectorTabIndex: selectorTabIndex
+        })
+    }
+
+    closeDslSelector = () => {
+        this.setState({showSelector: false})
+    }
+
+    onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
+        switch (dsl.dsl) {
+            case 'FromDefinition' :
+                const from = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
+                this.addStep(from, parentId, position)
+                break;
+            case 'ToDefinition' :
+                const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                this.addStep(to, parentId, position)
+                break;
+            case 'ToDynamicDefinition' :
+                const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                this.addStep(toD, parentId, position)
+                break;
+            case 'KameletDefinition' :
+                const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
+                this.addStep(kamelet, parentId, position)
+                break;
+            default:
+                const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
+                this.addStep(step, parentId, position)
+                break;
+        }
+    }
+
+    addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
+        const i = CamelDefinitionApiExt.addStepToIntegration(this.state.integration, step, parentId, position);
+        const clone = CamelUtil.cloneIntegration(i);
+        this.setState({
+            integration: clone,
+            key: Math.random().toString(),
+            showSelector: false,
+            selectedStep: step,
+            selectedUuid: step.uuid,
+            propertyOnly: false
+        });
+    }
+
+    onIntegrationUpdate = (i: Integration) => {
+        this.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
+    }
+
+    moveElement = (source: string, target: string, asChild: boolean) => {
+        const i = CamelDefinitionApiExt.moveRouteElement(this.state.integration, source, target, asChild);
+        const clone = CamelUtil.cloneIntegration(i);
+        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
+        this.setState({
+            integration: clone,
+            key: Math.random().toString(),
+            showSelector: false,
+            selectedStep: selectedStep,
+            selectedUuid: source,
+            propertyOnly: false
+        });
+    }
+
+    onResizePage(el: HTMLDivElement | null) {
+        const rect = el?.getBoundingClientRect();
+        if (el && rect && (el.scrollWidth !== this.state.width || el.scrollHeight !== this.state.height || rect.top !== this.state.top || rect.left !== this.state.left)) {
+            this.setState({width: el.scrollWidth, height: el.scrollHeight, top: rect.top, left: rect.left})
+        }
+    }
+
+    getSelectorModal() {
+        return (
+            <DslSelector
+                isOpen={this.state.showSelector}
+                onClose={() => this.closeDslSelector()}
+                dark={this.props.dark}
+                parentId={this.state.parentId}
+                parentDsl={this.state.parentDsl}
+                showSteps={this.state.showSteps}
+                position={this.state.selectedPosition}
+                tabIndex={this.state.selectorTabIndex}
+                onDslSelect={this.onDslSelect}/>)
+    }
+
+    getDeleteConfirmation() {
+        let htmlContent: string = this.state.deleteMessage;
+        return (<Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={this.state.showDeleteConfirmation}
+            onClose={() => this.setState({showDeleteConfirmation: false})}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => this.deleteElement()}>Delete</Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+            ]}
+            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            <div>
+                {htmlContent}
+            </div>
+        </Modal>)
+    }
+
+    getPropertiesPanel() {
+        return (
+            <DrawerPanelContent onResize={width => this.setState({key: Math.random().toString(1)})}
+                                style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
+                                maxSize={'800px'} minSize={'300px'}>
+                <DslProperties ref={this.state.ref}
+                               integration={this.state.integration}
+                               step={this.state.selectedStep}
+                               onIntegrationUpdate={this.onIntegrationUpdate}
+                               onPropertyUpdate={this.onPropertyUpdate}
+                               clipboardStep={this.state.clipboardStep}
+                               isRouteDesigner={true}
+                               onSaveClipboardStep={this.saveToClipboard}
+                               dark={this.props.dark}/>
+            </DrawerPanelContent>
+        )
+    }
+
+    downloadIntegrationImage(dataUrl: string) {
+        const a = document.createElement('a');
+        a.setAttribute('download', 'karavan-routes.png');
+        a.setAttribute('href', dataUrl);
+        a.click();
+    }
+
+    integrationImageDownloadFilter = (node: HTMLElement) => {
+        const exclusionClasses = ['add-flow'];
+        return !exclusionClasses.some(classname => {
+            return node.classList === undefined ? false: node.classList.contains(classname);
+        });
+    }
+
+    integrationImageDownload() {
+        if (this.state.printerRef.current === null) {
+            return
+        }
+        toPng(this.state.printerRef.current, { style:{overflow:'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
+                height:this.state.height,width:this.state.width,  backgroundColor: this.props.dark?"black":"white" }).then(v => {
+                    toPng(this.state.printerRef.current, { style:{overflow:'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
+                    height:this.state.height,width:this.state.width,  backgroundColor: this.props.dark?"black":"white" }).then(this.downloadIntegrationImage);
+            })
+    }
+
+    getGraph() {
+        const routes = CamelUi.getRoutes(this.state.integration);
+        return (
+            <div ref={this.state.printerRef} className="graph">
+                <DslConnections height={this.state.height} width={this.state.width} top={this.state.top}
+                                left={this.state.left} integration={this.state.integration}/>
+                <div className="flows" data-click="FLOWS" onClick={event => this.unselectElement(event)}
+                     ref={el => this.onResizePage(el)}>
+                    {routes?.map((route: any, index: number) => (
+                        <DslElement key={route.uuid + this.state.key}
+                                    integration={this.state.integration}
+                                    openSelector={this.openSelector}
+                                    deleteElement={this.showDeleteConfirmation}
+                                    selectElement={this.selectElement}
+                                    moveElement={this.moveElement}
+                                    selectedUuid={this.state.selectedUuid}
+                                    inSteps={false}
+                                    position={index}
+                                    step={route}
+                                    parent={undefined}/>
+                    ))}
+                    <div className="add-flow">
+                        <Button
+                            variant={routes.length === 0 ? "primary" : "secondary"}
+                            data-click="ADD_ROUTE"
+                            icon={<PlusIcon/>}
+                            onClick={e => this.openSelector(undefined, undefined)}>Create new route
+                        </Button>
+                    </div>
+                </div>
+            </div>)
+    }
+
+    render() {
+        return (
+            <PageSection className="dsl-page" isFilled padding={{default: 'noPadding'}}>
+                <div className="dsl-page-columns">
+                    <Drawer isExpanded isInline>
+                        <DrawerContent panelContent={this.getPropertiesPanel()}>
+                            <DrawerContentBody>{this.getGraph()}</DrawerContentBody>
+                        </DrawerContent>
+                    </Drawer>
+                </div>
+                {this.getSelectorModal()}
+                {this.getDeleteConfirmation()}
+            </PageSection>
+        );
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx b/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
new file mode 100644
index 0000000..67dd816
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption, InputGroup, TextArea, Tooltip, Button,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
+import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {ToDefinition} from "karavan-core/lib/model/CamelDefinition";
+import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
+import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
+import {KubernetesSelector} from "./KubernetesSelector";
+import {KubernetesAPI} from "../../utils/KubernetesAPI";
+import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon";
+import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
+import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
+
+const prefix = "parameters";
+const beanPrefix = "#bean:";
+
+interface Props {
+    property: ComponentProperty,
+    integration: Integration,
+    element?: CamelElement,
+    value: any,
+    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void
+}
+
+interface State {
+    selectStatus: Map<string, boolean>
+    showEditor: boolean
+    showPassword: boolean
+    showKubernetesSelector: boolean
+    kubernetesSelectorProperty?: string
+    ref: any
+}
+
+export class ComponentParameterField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectStatus: new Map<string, boolean>(),
+        showEditor: false,
+        showPassword: false,
+        showKubernetesSelector: false,
+        ref: React.createRef(),
+    }
+
+    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
+        this.props.onParameterChange?.call(this, parameter, value, pathParameter, newRoute);
+        this.setState({selectStatus: new Map<string, boolean>([[parameter, false]])});
+    }
+
+    openSelect = (propertyName: string, isExpanded: boolean) => {
+        this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
+    }
+
+    isSelectOpen = (propertyName: string): boolean => {
+        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    }
+
+    getSelectBean = (property: ComponentProperty, value: any) => {
+        const selectOptions: JSX.Element[] = [];
+        const beans = CamelUi.getBeans(this.props.integration);
+        if (beans) {
+            selectOptions.push(<SelectOption key={0} value={"Select..."} isPlaceholder/>);
+            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={beanPrefix + bean.name} description={bean.type}/>));
+        }
+        return (
+            <Select
+                variant={SelectVariant.single}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined))}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    canBeInternalUri = (property: ComponentProperty): boolean => {
+        if (this.props.element && this.props.element.dslName === 'ToDefinition' && property.name === 'name') {
+            const uri: string = (this.props.element as ToDefinition).uri || '';
+            return uri.startsWith("direct") || uri.startsWith("seda");
+        } else {
+            return false;
+        }
+    }
+
+    getInternalComponentName = (property: ComponentProperty): string => {
+        if (this.props.element && this.props.element.dslName === 'ToDefinition' && property.name === 'name') {
+            const uri: string = (this.props.element as ToDefinition).uri || '';
+            if (uri.startsWith("direct")) return "direct";
+            if (uri.startsWith("seda")) return "seda";
+            return '';
+        } else {
+            return '';
+        }
+    }
+
+    getInternalUriSelect = (property: ComponentProperty, value: any) => {
+        const selectOptions: JSX.Element[] = [];
+        const componentName = this.getInternalComponentName(property);
+        const urls = CamelUi.getInternalRouteUris(this.props.integration, componentName, false);
+        if (urls && urls.length > 0) {
+            selectOptions.push(...urls.map((value: string) =>
+                <SelectOption key={value} value={value.trim()}/>));
+        }
+        return (
+            <Select
+                placeholderText="Select or type an URI"
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => {
+                    const newRoute = !urls.includes(value.toString()) ? new RouteToCreate(componentName, value.toString()) : undefined;
+                    this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path', newRoute);
+                }}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                isCreatable={true}
+                isInputFilterPersisted={true}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    selectKubernetes = (value: string) => {
+        // check if there is a selection
+        const textVal = this.state.ref.current;
+        const cursorStart = textVal.selectionStart;
+        const cursorEnd = textVal.selectionEnd;
+        if (cursorStart !== cursorEnd){
+            const prevValue = this.props.value;
+            const selectedText = prevValue.substring(cursorStart, cursorEnd)
+            value = prevValue.replace(selectedText, value);
+        }
+        const propertyName = this.state.kubernetesSelectorProperty;
+        if (propertyName) {
+            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
+            this.parametersChanged(propertyName, value);
+            this.setState({showKubernetesSelector: false, kubernetesSelectorProperty: undefined})
+        }
+    }
+
+    openKubernetesSelector = (propertyName: string) => {
+        this.setState({kubernetesSelectorProperty: propertyName, showKubernetesSelector: true});
+    }
+
+    closeKubernetesSelector = () => {
+        this.setState({showKubernetesSelector: false})
+    }
+
+    getKubernetesSelectorModal() {
+        return (
+            <KubernetesSelector
+                dark={false}
+                isOpen={this.state.showKubernetesSelector}
+                onClose={() => this.closeKubernetesSelector()}
+                onSelect={this.selectKubernetes}/>)
+    }
+
+    getStringInput(property: ComponentProperty, value: any) {
+        const {showEditor, showPassword} = this.state;
+        const inKubernetes = KubernetesAPI.inKubernetes;
+        const id = prefix + "-" + property.name;
+        const noKubeSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
+        return <InputGroup>
+            {inKubernetes && !showEditor && !noKubeSelectorButton &&
+                <Tooltip position="bottom-end" content="Select from Kubernetes">
+                    <Button variant="control" onClick={e => this.openKubernetesSelector(property.name)}>
+                        <KubernetesIcon/>
+                    </Button>
+                </Tooltip>}
+            {(!showEditor || property.secret) &&
+                <TextInput className="text-field" isRequired ref={this.state.ref}
+                           type={property.secret && !showPassword ? "password" : "text"}
+                           id={id} name={id}
+                           value={value !== undefined ? value : property.defaultValue}
+                           onChange={e => this.parametersChanged(property.name, e, property.kind === 'path')}/>}
+            {showEditor && !property.secret &&
+                <TextArea autoResize={true} ref={this.state.ref}
+                          className="text-field" isRequired
+                          type="text"
+                          id={id} name={id}
+                          value={value !== undefined ? value : property.defaultValue}
+                          onChange={e => this.parametersChanged(property.name, e, property.kind === 'path')}/>}
+            {!property.secret &&
+                <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
+                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                    </Button>
+                </Tooltip>
+            }
+            {property.secret &&
+                <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
+                    <Button variant="control" onClick={e => this.setState({showPassword: !showPassword})}>
+                        {showPassword ? <ShowIcon/> : <HideIcon/>}
+                    </Button>
+                </Tooltip>
+            }
+        </InputGroup>
+    }
+
+    getTextInput = (property: ComponentProperty, value: any) => {
+        const id = prefix + "-" + property.name;
+        return (
+            <TextInput
+                className="text-field" isRequired
+                type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                id={id} name={id}
+                value={value !== undefined ? value : property.defaultValue}
+                onChange={e => this.parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(e) : e, property.kind === 'path')}/>
+        )
+    }
+
+    getSelect = (property: ComponentProperty, value: any) => {
+        const selectOptions: JSX.Element[] = []
+        if (property.enum && property.enum.length > 0) {
+            selectOptions.push(<SelectOption key={0} value={"Select ..."} isPlaceholder/>);
+            property.enum.forEach(v => selectOptions.push(<SelectOption key={v} value={v}/>));
+        }
+        return (
+            <Select
+                variant={SelectVariant.single}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path')}
+                selections={value !== undefined ? value.toString() : property.defaultValue}
+                isOpen={this.isSelectOpen(property.name)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    getSwitch = (property: ComponentProperty, value: any) => {
+        const id = prefix + "-" + property.name;
+        return (
+            <Switch
+                id={id} name={id}
+                value={value?.toString()}
+                aria-label={id}
+                isChecked={value !== undefined ? Boolean(value) === true : property.defaultValue !== undefined && property.defaultValue === 'true'}
+                onChange={e => this.parametersChanged(property.name, !Boolean(value))}/>
+        )
+    }
+
+    render() {
+        const property: ComponentProperty = this.props.property;
+        const value = this.props.value;
+        const id = prefix + "-" + property.name;
+        return (
+            <FormGroup
+                key={id}
+                label={property.displayName}
+                fieldId={id}
+                isRequired={property.required}
+                labelIcon={
+                    <Popover
+                        position={"left"}
+                        headerContent={property.displayName}
+                        bodyContent={property.description}
+                        footerContent={
+                            <div>
+                                {property.defaultValue !== undefined && <div>{"Default: " + property.defaultValue}</div>}
+                                {property.required === true && <div>{property.displayName + " is required"}</div>}
+                            </div>
+                        }>
+                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                                className="pf-c-form__group-label-help">
+                            <HelpIcon noVerticalAlign/>
+                        </button>
+                    </Popover>
+                }>
+                {this.canBeInternalUri(property) && this.getInternalUriSelect(property, value)}
+                {property.type === 'string' && property.enum === undefined && !this.canBeInternalUri(property)
+                    && this.getStringInput(property, value)}
+                {['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !this.canBeInternalUri(property)
+                    && this.getTextInput(property, value)}
+                {['object'].includes(property.type) && !property.enum
+                    && this.getSelectBean(property, value)}
+                {['string', 'object'].includes(property.type) && property.enum
+                    && this.getSelect(property, value)}
+                {property.type === 'boolean'
+                    && this.getSwitch(property, value)}
+                {this.getKubernetesSelectorModal()}
+            </FormGroup>
+        )
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/route/property/DataFormatField.tsx b/karavan-app/src/main/webui/src/designer/route/property/DataFormatField.tsx
new file mode 100644
index 0000000..aad0022
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/property/DataFormatField.tsx
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption, ExpandableSection,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import {CamelMetadataApi, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {DataFormatDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {DslPropertyField} from "./DslPropertyField";
+import {DataFormats} from "karavan-core/lib/model/CamelMetadata";
+
+interface Props {
+    dslName: string,
+    value: CamelElement,
+    onDataFormatChange?: (value: DataFormatDefinition) => void
+    integration: Integration,
+    dark: boolean,
+}
+
+interface State {
+    selectIsOpen: boolean
+    dataFormat: string
+    isShowAdvanced: boolean
+}
+
+export class DataFormatField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectIsOpen: false,
+        dataFormat: CamelDefinitionApiExt.getDataFormat(this.props.value)?.name || "json",
+        isShowAdvanced: false
+    }
+
+    componentDidMount() {
+        if (CamelDefinitionApiExt.getDataFormat(this.props.value)?.name === undefined) {
+            this.dataFormatChanged("json", CamelDefinitionApi.createDataFormat('JsonDataFormat', {}));
+        }
+    }
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        const newDataFormat = CamelDefinitionApiExt.getDataFormat(this.props.value)?.name || "json"
+        if (prevProps.value
+            && (prevProps.value.uuid !== this.props.value.uuid
+                || prevState.dataFormat !== newDataFormat)
+        ) {
+            this.setState({
+                dataFormat: newDataFormat
+            });
+        }
+    }
+
+    openSelect = () => {
+        this.setState({selectIsOpen: true});
+    }
+
+    dataFormatChanged = (dataFormat: string, value?: CamelElement) => {
+        if (dataFormat !== (value as any).dataFormatName) {
+            const className = CamelMetadataApi.getCamelDataFormatMetadataByName(dataFormat)?.className;
+            value = CamelDefinitionApi.createDataFormat(className || '', {}); // perhaps copy other similar fields later
+        }
+        const df = CamelDefinitionApi.createStep(this.props.dslName, {});
+        (df as any)[dataFormat] = value;
+        this.props.onDataFormatChange?.call(this, df);
+        this.setState({selectIsOpen: false});
+    }
+
+    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
+        const df = this.getDataFormatValue();
+        if (df) {
+            (df as any)[fieldId] = value;
+            this.dataFormatChanged(this.state.dataFormat, df);
+        }
+    }
+
+    getDataFormatValue = (): CamelElement => {
+        return (this.props.value as any)[this.state.dataFormat]
+            ? (this.props.value as any)[this.state.dataFormat]
+            : CamelDefinitionApi.createDataFormat(this.state.dataFormat, (this.props.value as any)[this.state.dataFormat]);
+    }
+
+    getPropertyFields = (value: any, properties: PropertyMeta[]) => {
+        return (<>
+            {value && properties?.map((property: PropertyMeta) =>
+                <DslPropertyField key={property.name} property={property}
+                                  integration={this.props.integration}
+                                  element={value}
+                                  value={value ? (value as any)[property.name] : undefined}
+                                  onExpressionChange={exp => {
+                                  }}
+                                  onParameterChange={parameter => {
+                                      console.log(parameter)
+                                  }}
+                                  onDataFormatChange={dataFormat => {
+                                      console.log(dataFormat)
+                                  }}
+                                  dark={this.props.dark}
+                                  onChange={this.propertyChanged}/>
+            )}
+        </>)
+    }
+
+    render() {
+        const value = this.getDataFormatValue();
+        const dataFormat = DataFormats.find((l: [string, string, string]) => l[0] === this.state.dataFormat);
+        const properties = CamelDefinitionApiExt.getElementPropertiesByName(this.state.dataFormat).sort((a, b) => a.name === 'library' ? -1 : 1);
+        const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
+        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+        const selectOptions: JSX.Element[] = []
+        DataFormats.forEach((lang: [string, string, string]) => {
+            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+            selectOptions.push(s);
+        })
+        return (
+            <div>
+                <div>
+                    <label className="pf-c-form__label" htmlFor="expression">
+                        <span className="pf-c-form__label-text">{"Data Format"}</span>
+                        <span className="pf-c-form__label-required" aria-hidden="true"> *</span>
+                    </label>
+                    <Select
+                        variant={SelectVariant.typeahead}
+                        aria-label={"dataFormat"}
+                        onToggle={() => {
+                            this.openSelect()
+                        }}
+                        onSelect={(e, dataFormat, isPlaceholder) => this.dataFormatChanged(dataFormat.toString(), value)}
+                        selections={dataFormat}
+                        isOpen={this.state.selectIsOpen}
+                        aria-labelledby={"dataFormat"}
+                        direction={SelectDirection.down}
+                    >
+                        {selectOptions}
+                    </Select>
+                </div>
+                <div className="object">
+                    <div>
+                        {this.getPropertyFields(value, propertiesMain)}
+                        {propertiesAdvanced.length > 0 &&
+                            <ExpandableSection
+                                toggleText={'Advanced properties'}
+                                onToggle={isExpanded => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
+                                isExpanded={this.state.isShowAdvanced}>
+                                {this.getPropertyFields(value, propertiesAdvanced)}
+                            </ExpandableSection>}
+                    </div>
+
+                </div>
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx b/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
new file mode 100644
index 0000000..9f4504e
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -0,0 +1,695 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption,
+    ExpandableSection,
+    TextArea,
+    Chip,
+    TextInputGroup,
+    TextInputGroupMain,
+    TextInputGroupUtilities,
+    ChipGroup,
+    Button,
+    Text,
+    Tooltip,
+    Card,
+    InputGroup,
+    Modal,
+    ModalVariant, Title, TitleSizes
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {ExpressionField} from "./ExpressionField";
+import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
+import {ComponentParameterField} from "./ComponentParameterField";
+import {DataFormatDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {KameletPropertyField} from "./KameletPropertyField";
+import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
+import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {ObjectField} from "./ObjectField";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import {MediaTypes} from "../../utils/MediaTypes";
+import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
+import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
+import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
+import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon";
+import {KubernetesSelector} from "./KubernetesSelector";
+import {KubernetesAPI} from "../../utils/KubernetesAPI";
+import Editor from "@monaco-editor/react";
+import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
+
+interface Props {
+    property: PropertyMeta,
+    value: any,
+    onChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
+    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void,
+    onDataFormatChange?: (value: DataFormatDefinition) => void,
+    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void,
+    element?: CamelElement
+    integration: Integration,
+    hideLabel?: boolean,
+    dslLanguage?: [string, string, string],
+    dark: boolean
+}
+
+interface State {
+    selectStatus: Map<string, boolean>,
+    isShowAdvanced: Map<string, boolean>,
+    arrayValues: Map<string, string>,
+    showEditor: boolean
+    showKubernetesSelector: boolean
+    kubernetesSelectorProperty?: string
+    ref: any
+}
+
+export class DslPropertyField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectStatus: new Map<string, boolean>(),
+        arrayValues: new Map<string, string>(),
+        isShowAdvanced: new Map<string, boolean>(),
+        showEditor: false,
+        showKubernetesSelector: false,
+        ref: React.createRef(),
+    };
+
+    openSelect = (propertyName: string, isExpanded: boolean) => {
+        this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
+    }
+
+    clearSelection = (propertyName: string) => {
+        this.setState({selectStatus: new Map<string, boolean>([[propertyName, false]])});
+    };
+
+    isSelectOpen = (propertyName: string): boolean => {
+        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    }
+
+    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
+        this.props.onChange?.call(this, fieldId, value, newRoute);
+        this.setState({selectStatus: new Map<string, boolean>([[fieldId, false]])});
+    }
+
+    arrayChanged = (fieldId: string, value: string) => {
+        const tv = this.state.arrayValues;
+        tv.set(fieldId, value);
+        this.setState({arrayValues: tv});
+    }
+
+    arrayDeleteValue = (fieldId: string, element: string) => {
+        const property: PropertyMeta = this.props.property;
+        let value = this.props.value;
+        if (property.isArray && property.type === 'string') {
+            this.propertyChanged(fieldId, (value as any).filter((x: string) => x !== element))
+        }
+    }
+
+    arraySave = (fieldId: string) => {
+        const newValue = this.state.arrayValues.get(fieldId);
+        const property: PropertyMeta = this.props.property;
+        let value = this.props.value;
+        if (property.isArray && property.type === 'string') {
+            if (value) (value as any).push(newValue)
+            else value = [newValue];
+        }
+        this.propertyChanged(fieldId, value);
+        this.arrayChanged(fieldId, "");
+    }
+
+    getLabel = (property: PropertyMeta, value: any) => {
+        if (!this.isMultiValueField(property) && property.isObject && !property.isArray && !["ExpressionDefinition"].includes(property.type)) {
+            const tooltip = value ? "Delete " + property.name : "Add " + property.name;
+            const x = value ? undefined : CamelDefinitionApi.createStep(property.type, {});
+            const icon = value ? (<DeleteIcon noVerticalAlign/>) : (<AddIcon noVerticalAlign/>);
+            return (
+                <div style={{display: "flex"}}>
+                    <Text>{property.displayName} </Text>
+                    <Tooltip position={"bottom"} content={<div>{tooltip}</div>}>
+                        <button className="add-button" onClick={e => this.props.onChange?.call(this, property.name, x)} aria-label="Add element">
+                            {icon}
+                        </button>
+                    </Tooltip>
+                </div>
+            )
+        } else if (!["ExpressionDefinition"].includes(property.type)) {
+            return CamelUtil.capitalizeName(property.displayName);
+        }
+    }
+
+    isUriReadOnly = (property: PropertyMeta): boolean => {
+        const dslName: string = this.props.element?.dslName || '';
+        return property.name === 'uri' && !['ToDynamicDefinition', 'WireTapDefinition'].includes(dslName)
+    }
+
+    selectKubernetes = (value: string) => {
+        // check if there is a selection
+        const textVal = this.state.ref.current;
+        const cursorStart = textVal.selectionStart;
+        const cursorEnd = textVal.selectionEnd;
+        if (cursorStart !== cursorEnd) {
+            const prevValue = this.props.value;
+            const selectedText = prevValue.substring(cursorStart, cursorEnd)
+            value = prevValue.replace(selectedText, value);
+        }
+        const propertyName = this.state.kubernetesSelectorProperty;
+        if (propertyName) {
+            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
+            this.propertyChanged(propertyName, value);
+            this.setState({showKubernetesSelector: false, kubernetesSelectorProperty: undefined})
+        }
+    }
+
+    openKubernetesSelector = (propertyName: string) => {
+        this.setState({kubernetesSelectorProperty: propertyName, showKubernetesSelector: true});
+    }
+
+    closeKubernetesSelector = () => {
+        this.setState({showKubernetesSelector: false})
+    }
+
+    getKubernetesSelectorModal() {
+        return (
+            <KubernetesSelector
+                dark={false}
+                isOpen={this.state.showKubernetesSelector}
+                onClose={() => this.closeKubernetesSelector()}
+                onSelect={this.selectKubernetes}/>)
+    }
+
+    getStringInput = (property: PropertyMeta, value: any) => {
+        const showEditor = this.state.showEditor;
+        const inKubernetes = KubernetesAPI.inKubernetes;
+        const noKubeSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
+        return (<InputGroup>
+            {inKubernetes && !showEditor && !noKubeSelectorButton &&
+                <Tooltip position="bottom-end" content="Select from Kubernetes">
+                    <Button variant="control" onClick={e => this.openKubernetesSelector(property.name)}>
+                        <KubernetesIcon/>
+                    </Button>
+                </Tooltip>}
+            {(!showEditor || property.secret) && <TextInput
+                ref={this.state.ref}
+                className="text-field" isRequired isReadOnly={this.isUriReadOnly(property)}
+                type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                onChange={e => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(e) : e)}/>
+            }
+            {showEditor && !property.secret && <TextArea
+                ref={this.state.ref}
+                autoResize={true}
+                className="text-field" isRequired isReadOnly={this.isUriReadOnly(property)}
+                type="text"
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                onChange={e => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(e) : e)}/>
+            }
+            {!property.secret &&
+                <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
+                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                    </Button>
+                </Tooltip>
+            }
+        </InputGroup>)
+    }
+
+    getTextArea = (property: PropertyMeta, value: any) => {
+        const {dslLanguage, dark} = this.props;
+        const showEditor = this.state.showEditor;
+        return (
+            <InputGroup>
+                <TextArea
+                    autoResize
+                    className="text-field" isRequired
+                    type={"text"}
+                    id={property.name} name={property.name}
+                    height={"100px"}
+                    value={value?.toString()}
+                    onChange={e => this.propertyChanged(property.name, e)}/>
+                <Tooltip position="bottom-end" content={"Show Editor"}>
+                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                        <EditorIcon/>
+                    </Button>
+                </Tooltip>
+                <Modal
+                    variant={ModalVariant.large}
+                    header={<React.Fragment>
+                        <Title id="modal-custom-header-label" headingLevel="h1" size={TitleSizes['2xl']}>
+                            {`Expression (${dslLanguage?.[0]})`}
+                        </Title>
+                        <p className="pf-u-pt-sm">{dslLanguage?.[2]}</p>
+                    </React.Fragment>}
+                    isOpen={this.state.showEditor}
+                    onClose={() => this.setState({showEditor: false})}
+                    actions={[
+                        <Button key="cancel" variant="primary" isSmall
+                                onClick={e => this.setState({showEditor: false})}>Close</Button>
+                    ]}
+                    onEscapePress={e => this.setState({showEditor: false})}>
+                    <Editor
+                        height="400px"
+                        width="100%"
+                        defaultLanguage={'java'}
+                        language={'java'}
+                        theme={dark ? 'vs-dark' : 'light'}
+                        options={{lineNumbers:"off", folding:false, lineNumbersMinChars:10, showUnused:false, fontSize:12, minimap:{enabled:false}}}
+                        value={value?.toString()}
+                        className={'code-editor'}
+                        onChange={(value: any, ev: any) => {
+                            if (value) {
+                                this.propertyChanged(property.name, value)
+                            }
+                        }}
+                    />
+                </Modal>
+            </InputGroup>
+        )
+    }
+
+    getExpressionField = (property: PropertyMeta, value: any) => {
+        return (
+            <div className="expression">
+                <ExpressionField property={property}
+                                 value={value}
+                                 onExpressionChange={this.props.onExpressionChange}
+                                 integration={this.props.integration}
+                                 dark={this.props.dark}/>
+            </div>
+        )
+    }
+
+    getObjectField = (property: PropertyMeta, value: any) => {
+        return (
+            <div className="object">
+                {value && <ObjectField property={property}
+                                       value={value}
+                                       onPropertyUpdate={this.props.onChange}
+                                       integration={this.props.integration}
+                                       dark={this.props.dark}/>}
+            </div>
+        )
+    }
+
+    getSwitch = (property: PropertyMeta, value: any) => {
+        const isChecked = value !== undefined ? Boolean(value) : Boolean(property.defaultValue !== undefined && property.defaultValue === 'true');
+        return (
+            <Switch
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                aria-label={property.name}
+                isChecked={isChecked}
+                onChange={e => this.propertyChanged(property.name, e)}/>
+        )
+    }
+
+    getSelectBean = (property: PropertyMeta, value: any) => {
+        const selectOptions: JSX.Element[] = [];
+        const beans = CamelUi.getBeans(this.props.integration);
+        if (beans) {
+            selectOptions.push(<SelectOption key={0} value={"Select..."} isPlaceholder/>);
+            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={bean.name} description={bean.type}/>));
+        }
+        return (
+            <Select
+                variant={SelectVariant.single}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    getSelect = (property: PropertyMeta, value: any) => {
+        const selectOptions: JSX.Element[] = []
+        if (property.enumVals && property.enumVals.length > 0) {
+            selectOptions.push(<SelectOption key={0} value={"Select " + property.name} isPlaceholder/>);
+            selectOptions.push(...property.enumVals.split(',').map((value: string) =>
+                <SelectOption key={value} value={value.trim()}/>));
+        }
+        return (
+            <Select
+                variant={SelectVariant.single}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    getMediaTypeSelectOptions(filter?: string) {
+        return filter
+            ? MediaTypes.filter(mt => mt.includes(filter)).map((value: string) => <SelectOption key={value} value={value.trim()}/>)
+            : MediaTypes.map((value: string) => <SelectOption key={value} value={value.trim()}/>);
+    }
+
+    getMediaTypeSelect = (property: PropertyMeta, value: any) => {
+        return (
+            <Select
+                placeholderText="Select Media Type"
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                isCreatable={false}
+                isInputFilterPersisted={false}
+                onFilter={(e, text) => this.getMediaTypeSelectOptions(text)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {this.getMediaTypeSelectOptions()}
+            </Select>
+        )
+    }
+
+    canBeInternalUri = (property: PropertyMeta, element?: CamelElement): boolean => {
+        if (element?.dslName === 'WireTapDefinition' && property.name === 'uri') {
+            return true;
+        } else if (element?.dslName === 'SagaDefinition' && ['compensation', 'completion'].includes(property.name)) {
+            return true;
+        } else if (element && ['GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element?.dslName) && property.name === 'to') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    canBeMediaType = (property: PropertyMeta, element?: CamelElement): boolean => {
+        if (element
+            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element?.dslName)
+            && ['consumes', 'produces'].includes(property.name)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    getInternalUriSelect = (property: PropertyMeta, value: any) => {
+        const selectOptions: JSX.Element[] = [];
+        const urls = CamelUi.getInternalRouteUris(this.props.integration, "direct");
+        urls.push(...CamelUi.getInternalRouteUris(this.props.integration, "seda"));
+        if (urls && urls.length > 0) {
+            selectOptions.push(...urls.map((value: string) =>
+                <SelectOption key={value} value={value.trim()}/>));
+        }
+        return (
+            <Select
+                placeholderText="Select or type an URI"
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onClear={event => this.clearSelection(property.name)}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => {
+                    const url = value.toString().split(":");
+                    const newRoute = !urls.includes(value.toString()) && (['direct', 'seda'].includes(url[0])) ? new RouteToCreate(url[0], url[1]) : undefined;
+                    this.propertyChanged(property.name, (!isPlaceholder ? value : undefined), newRoute)
+                }}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                isCreatable={true}
+                isInputFilterPersisted={true}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    onMultiValueObjectUpdate = (index: number, fieldId: string, value: CamelElement) => {
+        const mValue = [...this.props.value];
+        mValue[index] = value;
+        this.props.onChange?.call(this, fieldId, mValue);
+    }
+
+    isKeyValueObject(property: PropertyMeta) {
+        const props = CamelDefinitionApiExt.getElementProperties(property.type);
+        return props.length === 2 && props.filter(p => p.name === 'key').length === 1 && props.filter(p => p.name === 'value').length === 1;
+    }
+
+    getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, index: number, hideLabel: boolean = false) {
+        return (<>
+            <div className="object">
+                {value && <ObjectField property={property}
+                                       hideLabel={hideLabel}
+                                       value={v}
+                                       onPropertyUpdate={(f, v) => this.onMultiValueObjectUpdate(index, f, v)}
+                                       integration={this.props.integration}
+                                       dark={this.props.dark}/>}
+            </div>
+            <Button variant="link" className="delete-button" onClick={e => {
+                const v = Array.from(value);
+                v.splice(index, 1);
+                this.propertyChanged(property.name, v);
+            }}><DeleteIcon/></Button>
+        </>)
+    }
+
+    getMultiValueObjectField = (property: PropertyMeta, value: any) => {
+        const isKeyValue = this.isKeyValueObject(property);
+        return (
+            <div>
+                {value && Array.from(value).map((v: any, index: number) => {
+                    if (isKeyValue)
+                        return <div key={property + "-" + index} className="object-key-value">
+                            {this.getMultiObjectFieldProps(property, value, v, index, index > 0)}
+                        </div>
+                    else
+                        return <Card key={property + "-" + index} className="object-value">
+                            {this.getMultiObjectFieldProps(property, value, v, index)}
+                        </Card>
+                })}
+                <Button variant="link" className="add-button"
+                        onClick={e => this.propertyChanged(property.name, [...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add " + property.displayName}
+                </Button>
+            </div>
+        )
+    }
+
+    getMultiValueField = (property: PropertyMeta, value: any) => {
+        return (
+            <div>
+                <TextInputGroup className="input-group">
+                    <TextInputGroupMain value={this.state.arrayValues.get(property.name)} onChange={e => this.arrayChanged(property.name, e)} onKeyUp={e => {
+                        if (e.key === 'Enter') this.arraySave(property.name)
+                    }}>
+                        <ChipGroup>
+                            {value && Array.from(value).map((v: any, index: number) => (
+                                <Chip key={"chip-" + index} className="chip" onClick={() => this.arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
+                        </ChipGroup>
+                    </TextInputGroupMain>
+                    <TextInputGroupUtilities>
+                        <Button variant="plain" onClick={e => this.arraySave(property.name)} aria-label="Add element">
+                            <PlusIcon/>
+                        </Button>
+                    </TextInputGroupUtilities>
+                </TextInputGroup>
+            </div>
+        )
+    }
+
+    getKameletParameters = () => {
+        const element = this.props.element;
+        const requiredParameters = CamelUtil.getKameletRequiredParameters(element);
+        return (
+            <div className="parameters">
+                {CamelUtil.getKameletProperties(element).map(property =>
+                    <KameletPropertyField
+                        key={property.id}
+                        property={property}
+                        value={CamelDefinitionApiExt.getParametersValue(element, property.id)}
+                        required={requiredParameters?.includes(property.id)}
+                        onParameterChange={this.props.onParameterChange}
+                    />)}
+            </div>
+        )
+    }
+
+    getMainComponentParameters = (properties: ComponentProperty[]) => {
+        return (
+            <div className="parameters">
+                {properties.map(kp => {
+                    // console.log(kp);
+                    // console.log(CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path'));
+                    return (<ComponentParameterField
+                        key={kp.name}
+                        property={kp}
+                        element={this.props.element}
+                        integration={this.props.integration}
+                        value={CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path')}
+                        onParameterChange={this.props.onParameterChange}
+                    />)
+                })}
+            </div>
+        )
+    }
+
+    getExpandableComponentParameters = (properties: ComponentProperty[], label: string) => {
+        return (
+            <ExpandableSection
+                toggleText={label}
+                onToggle={isExpanded => {
+                    this.setState(state => {
+                        state.isShowAdvanced.set(label, !state.isShowAdvanced.get(label));
+                        return {isShowAdvanced: state.isShowAdvanced};
+                    })
+                }}
+                isExpanded={this.state.isShowAdvanced.has(label) && this.state.isShowAdvanced.get(label)}>
+                <div className="parameters">
+                    {properties.map(kp =>
+                        <ComponentParameterField
+                            key={kp.name}
+                            property={kp}
+                            integration={this.props.integration}
+                            value={CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path')}
+                            onParameterChange={this.props.onParameterChange}
+                        />
+                    )}
+                </div>
+            </ExpandableSection>
+        )
+    }
+
+    getLabelIcon = (property: PropertyMeta) => {
+        return (
+            property.description
+                ? <Popover
+                    position={"left"}
+                    headerContent={property.displayName}
+                    bodyContent={property.description}
+                    footerContent={
+                        <div>
+                            {property.defaultValue !== undefined && property.defaultValue.toString().trim().length > 0 && <div>{"Default: " + property.defaultValue}</div>}
+                            {property.required && <b>Required</b>}
+                        </div>
+                    }>
+                    <button type="button" aria-label="More info" onClick={e => {
+                        e.preventDefault();
+                        e.stopPropagation();
+                    }} className="pf-c-form__group-label-help">
+                        <HelpIcon noVerticalAlign/>
+                    </button>
+                </Popover>
+                : <div></div>
+        )
+    }
+
+
+    isMultiValueField = (property: PropertyMeta): boolean => {
+        return ['string'].includes(property.type) && property.name !== 'expression' && property.isArray && !property.enumVals;
+    }
+
+    getComponentParameters(property: PropertyMeta) {
+        const properties = CamelUtil.getComponentProperties(this.props.element);
+        const propertiesMain = properties.filter(p => !p.label.includes("advanced") && !p.label.includes("security") && !p.label.includes("scheduler"));
+        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+        const propertiesScheduler = properties.filter(p => p.label.includes("scheduler"));
+        const propertiesSecurity = properties.filter(p => p.label.includes("security"));
+        return (
+            <>
+                {property.name === 'parameters' && this.getMainComponentParameters(propertiesMain)}
+                {property.name === 'parameters' && this.props.element && propertiesScheduler.length > 0
+                    && this.getExpandableComponentParameters(propertiesScheduler, "Scheduler parameters")}
+                {property.name === 'parameters' && this.props.element && propertiesSecurity.length > 0
+                    && this.getExpandableComponentParameters(propertiesSecurity, "Security parameters")}
+                {property.name === 'parameters' && this.props.element && propertiesAdvanced.length > 0
+                    && this.getExpandableComponentParameters(propertiesAdvanced, "Advanced parameters")}
+            </>
+        )
+    }
+
+    render() {
+        const isKamelet = CamelUtil.isKameletComponent(this.props.element);
+        const property: PropertyMeta = this.props.property;
+        const value = this.props.value;
+        return (
+            <div>
+                <FormGroup
+                    label={this.props.hideLabel ? undefined : this.getLabel(property, value)}
+                    isRequired={property.required}
+                    fieldId={property.name}
+                    labelIcon={this.getLabelIcon(property)}>
+                    {value && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                        && this.getExpressionField(property, value)}
+                    {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                        && this.getObjectField(property, value)}
+                    {property.isObject && property.isArray && !this.isMultiValueField(property)
+                        && this.getMultiValueObjectField(property, value)}
+                    {property.name === 'expression' && property.type === "string" && !property.isArray
+                        && this.getTextArea(property, value)}
+                    {this.canBeInternalUri(property, this.props.element)
+                        && this.getInternalUriSelect(property, value)}
+                    {this.canBeMediaType(property, this.props.element)
+                        && this.getMediaTypeSelect(property, value)}
+                    {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref")
+                        && !property.isArray && !property.enumVals
+                        && !this.canBeInternalUri(property, this.props.element)
+                        && !this.canBeMediaType(property, this.props.element)
+                        && this.getStringInput(property, value)}
+                    {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals
+                        && this.getSelectBean(property, value)}
+                    {this.isMultiValueField(property)
+                        && this.getMultiValueField(property, value)}
+                    {property.type === 'boolean'
+                        && this.getSwitch(property, value)}
+                    {property.enumVals
+                        && this.getSelect(property, value)}
+                    {isKamelet && property.name === 'parameters' && this.getKameletParameters()}
+                    {!isKamelet && property.name === 'parameters' && this.getComponentParameters(property)}
+                </FormGroup>
+                {this.getKubernetesSelectorModal()}
+            </div>
+        )
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/route/property/ExpressionField.tsx b/karavan-app/src/main/webui/src/designer/route/property/ExpressionField.tsx
new file mode 100644
index 0000000..95cf638
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/property/ExpressionField.tsx
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    Popover,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {CamelMetadataApi, Languages, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {DslPropertyField} from "./DslPropertyField";
+import {CamelUi} from "../../utils/CamelUi";
+
+interface Props {
+    property: PropertyMeta,
+    value: CamelElement,
+    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void
+    integration: Integration,
+    dark: boolean,
+}
+
+interface State {
+    selectIsOpen: boolean;
+}
+
+export class ExpressionField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectIsOpen: false,
+    }
+
+    openSelect = (isExpanded: boolean) => {
+        this.setState({selectIsOpen: isExpanded});
+    }
+
+    expressionChanged = (language: string, value: CamelElement) => {
+        if (language !== (value as any).expressionName) {
+            const className = CamelMetadataApi.getCamelLanguageMetadataByName(language)?.className;
+            value = CamelDefinitionApi.createExpression(className || '', {expression: (value as any).expression}); // perhaps copy other similar fields later
+        }
+        const exp = new ExpressionDefinition();
+        (exp as any)[language] = value;
+        if (this.props.value) (exp as any).uuid = this.props.value.uuid;
+        this.props.onExpressionChange?.call(this, this.props.property.name, exp);
+        this.setState({selectIsOpen: false});
+    }
+
+    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
+        const expression = this.getExpressionValue();
+        if (expression) {
+            (expression as any)[fieldId] = value;
+            this.expressionChanged(this.getValueLanguage(), expression);
+        }
+    }
+
+    getValueClassName = (): string => {
+        return CamelDefinitionApiExt.getExpressionLanguageClassName(this.props.value) || 'SimpleExpression';
+    }
+
+    getValueLanguage = (): string => {
+        return CamelDefinitionApiExt.getExpressionLanguageName(this.props.value) || 'simple';
+    }
+
+    getExpressionValue = (): CamelElement => {
+        const language = this.getValueLanguage();
+        return this.props.value && (this.props.value as any)[language]
+            ? (this.props.value as any)[language]
+            : CamelDefinitionApi.createExpression(this.getValueClassName(), this.props.value);
+    }
+
+    getProps = (): PropertyMeta[] => {
+        const dslName = this.getValueClassName();
+        return CamelDefinitionApiExt.getElementProperties(dslName)
+            .filter(p => p.name !== 'id')
+            .filter(p => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'));
+    }
+
+    render() {
+        const property: PropertyMeta = this.props.property;
+        const value = this.getExpressionValue();
+        const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === this.getValueLanguage());
+        const selectOptions: JSX.Element[] = []
+        Languages.forEach((lang: [string, string, string]) => {
+            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+            selectOptions.push(s);
+        })
+        return (
+            <div>
+                <label className="pf-c-form__label" htmlFor="expression">
+                    <span className="pf-c-form__label-text">Language</span>
+                    <span className="pf-c-form__label-required" aria-hidden="true"> *</span>
+                </label>
+                <Select
+                    variant={SelectVariant.typeahead}
+                    aria-label={property.name}
+                    onToggle={isExpanded => {
+                        this.openSelect(isExpanded)
+                    }}
+                    onSelect={(e, lang, isPlaceholder) => {
+                        this.expressionChanged(lang.toString(), value);
+                    }}
+                    selections={dslLanguage}
+                    isOpen={this.state.selectIsOpen}
+                    aria-labelledby={property.name}
+                    direction={SelectDirection.down}
+                >
+                    {selectOptions}
+                </Select>
+                <FormGroup
+                    key={property.name}
+                    fieldId={property.name}
+                    labelIcon={property.description ?
+                        <Popover
+                            position={"left"}
+                            headerContent={property.displayName}
+                            bodyContent={property.description}>
+                            <button type="button" aria-label="More info" onClick={e => {
+                                e.preventDefault();
+                                e.stopPropagation();
+                            }}
+                                    className="pf-c-form__group-label-help">
+                                <HelpIcon noVerticalAlign/>
+                            </button>
+                        </Popover> : <div></div>
+                    }>
+                    {value && this.getProps().map((property: PropertyMeta) =>
+                        <DslPropertyField key={property.name + this.props.value?.uuid} property={property}
+                                          integration={this.props.integration}
+                                          element={value}
+                                          value={value ? (value as any)[property.name] : undefined}
+                                          onExpressionChange={exp => {}}
+                                          onParameterChange={parameter => {console.log(parameter)}}
+                                          onDataFormatChange={dataFormat => {console.log(dataFormat)}}
+                                          onChange={this.propertyChanged}
+                                          dark={this.props.dark}
+                                          dslLanguage={dslLanguage}/>
+                    )}
+                </FormGroup>
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx b/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
new file mode 100644
index 0000000..cda5ac3
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch, InputGroup, Button, TextArea, Tooltip
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
+import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
+import {Property} from "karavan-core/lib/model/KameletModels";
+import {KubernetesSelector} from "./KubernetesSelector";
+import {KubernetesAPI} from "../../utils/KubernetesAPI";
+import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon";
+import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
+import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
+
+interface Props {
+    property: Property,
+    value: any,
+    required: boolean,
+    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => void
+}
+
+interface State {
+    selectIsOpen: boolean
+    showEditor: boolean
+    showPassword: boolean
+    showKubernetesSelector: boolean
+    kubernetesSelectorProperty?: string
+    ref: any
+}
+
+export class KameletPropertyField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectIsOpen: false,
+        showEditor: false,
+        showPassword: false,
+        showKubernetesSelector: false,
+        ref: React.createRef(),
+    }
+
+    openSelect = () => {
+        this.setState({selectIsOpen: true});
+    }
+
+    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => {
+        this.props.onParameterChange?.call(this, parameter, value, pathParameter);
+        this.setState({selectIsOpen: false});
+    }
+
+    selectKubernetes = (value: string) => {
+        // check if there is a selection
+        const textVal = this.state.ref.current;
+        const cursorStart = textVal.selectionStart;
+        const cursorEnd = textVal.selectionEnd;
+        if (cursorStart !== cursorEnd){
+            const prevValue = this.props.value;
+            const selectedText = prevValue.substring(cursorStart, cursorEnd)
+            value = prevValue.replace(selectedText, value);
+        }
+        const propertyId = this.state.kubernetesSelectorProperty;
+        if (propertyId){
+            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
+            this.parametersChanged(propertyId, value);
+            this.setState({showKubernetesSelector: false, kubernetesSelectorProperty: undefined})
+        }
+    }
+
+    openKubernetesSelector = (propertyName: string) => {
+        this.setState({kubernetesSelectorProperty: propertyName, showKubernetesSelector: true});
+    }
+
+    closeKubernetesSelector = () => {
+        this.setState({showKubernetesSelector: false})
+    }
+
+    getKubernetesSelectorModal() {
+        return (
+            <KubernetesSelector
+                dark={false}
+                isOpen={this.state.showKubernetesSelector}
+                onClose={() => this.closeKubernetesSelector()}
+                onSelect={this.selectKubernetes}/>)
+    }
+
+    getStringInput() {
+        const {showEditor, showPassword} = this.state;
+        const {property, value} = this.props;
+        const prefix = "parameters";
+        const id = prefix + "-" + property.id;
+        const noKubeSelectorButton = ["uri", "id", "description", "group"].includes(property.id);
+        const showKubeSelectorButton = KubernetesAPI.inKubernetes && !showEditor && !noKubeSelectorButton
+        return <InputGroup>
+            {showKubeSelectorButton  &&
+                <Tooltip position="bottom-end" content="Select from Kubernetes">
+                    <Button variant="control" onClick={e => this.openKubernetesSelector(property.id)}>
+                        <KubernetesIcon/>
+                    </Button>
+                </Tooltip>}
+            {(!showEditor || property.format === "password") &&
+                <TextInput
+                    ref={this.state.ref}
+                    className="text-field" isRequired
+                    type={property.format && !showPassword ? "password" : "text"}
+                    id={id} name={id}
+                    value={value}
+                    onChange={e => this.parametersChanged(property.id, e)}/>}
+            {showEditor && property.format !== "password" &&
+                <TextArea autoResize={true}
+                          className="text-field" isRequired
+                          type="text"
+                          id={id} name={id}
+                          value={value}
+                          onChange={e => this.parametersChanged(property.id, e)}/>}
+            {property.format !== "password" &&
+                <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
+                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                    </Button>
+                </Tooltip>
+            }
+            {property.format === "password" &&
+                <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
+                    <Button variant="control" onClick={e => this.setState({showPassword: !showPassword})}>
+                        {showPassword ? <ShowIcon/> : <HideIcon/>}
+                    </Button>
+                </Tooltip>
+            }
+        </InputGroup>
+    }
+
+    render() {
+        const property = this.props.property;
+        const value = this.props.value;
+        const prefix = "parameters";
+        const id = prefix + "-" + property.id;
+        return (
+            <div>
+                <FormGroup
+                    key={id}
+                    label={property.title}
+                    fieldId={id}
+                    isRequired={this.props.required}
+                    labelIcon={
+                        <Popover
+                            position={"left"}
+                            headerContent={property.title}
+                            bodyContent={property.description}
+                            footerContent={
+                                <div>
+                                    {property.default !== undefined &&
+                                        <div>Default: {property.default.toString()}</div>}
+                                    {property.example !== undefined && <div>Example: {property.example}</div>}
+                                </div>
+                            }>
+                            <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                                    className="pf-c-form__group-label-help">
+                                <HelpIcon noVerticalAlign/>
+                            </button>
+                        </Popover>
+                    }>
+                    {property.type === 'string' && this.getStringInput()
+                    }
+                    {['integer', 'int', 'number'].includes(property.type) &&
+                        <TextInput className="text-field" isRequired type='number' id={id} name={id} value={value}
+                                   onChange={e => this.parametersChanged(property.id, Number(e))}
+                        />
+                    }
+                    {property.type === 'boolean' && <Switch
+                        id={id} name={id}
+                        value={value?.toString()}
+                        aria-label={id}
+                        isChecked={Boolean(value) === true}
+                        onChange={e => this.parametersChanged(property.id, !Boolean(value))}/>
+                    }
+                </FormGroup>
+                {this.getKubernetesSelectorModal()}
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/property/KubernetesSelector.tsx b/karavan-app/src/main/webui/src/designer/route/property/KubernetesSelector.tsx
new file mode 100644
index 0000000..a609157
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/property/KubernetesSelector.tsx
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Badge,
+    Button, Flex, FlexItem,
+    Form, FormGroup, Modal, PageSection,
+    Tab, Tabs, TabTitleText, TextInput,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import {TableComposable, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table";
+import {KubernetesAPI} from "../../utils/KubernetesAPI";
+
+interface Props {
+    onSelect: (value: string) => void,
+    onClose?: () => void,
+    isOpen: boolean,
+    dark: boolean,
+}
+
+interface State {
+    tabIndex: string | number
+    filter?: string
+    configMaps:  string[]
+    secrets:  string[]
+    services:  string[]
+}
+
+export class KubernetesSelector extends React.Component<Props, State> {
+
+    public state: State = {
+        tabIndex: "configMap",
+        configMaps: KubernetesAPI.configMaps,
+        secrets: KubernetesAPI.secrets,
+        services: KubernetesAPI.services
+    };
+
+    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
+        this.setState({tabIndex: eventKey})
+    }
+
+    checkFilter = (name: string): boolean => {
+        if (this.state.filter !== undefined && name) {
+            return name.toLowerCase().includes(this.state.filter.toLowerCase())
+        } else {
+            return true;
+        }
+    }
+
+    searchInput = () => {
+        return (
+            <Form isHorizontal className="search" autoComplete="off">
+                <FormGroup fieldId="search">
+                    <TextInput className="text-field" type="text" id="search" name="search" iconVariant='search'
+                               value={this.state.filter}
+                               onChange={e => this.setState({filter: e})}/>
+                </FormGroup>
+            </Form>
+        )
+    }
+
+    getConfigMapTable() {
+        const configMaps = this.state.configMaps;
+        return (
+            <TableComposable variant='compact' borders={false}>
+                <Thead>
+                    <Tr>
+                        <Th/>
+                        <Th key='name'>Name</Th>
+                        <Th key='data'>Data</Th>
+                    </Tr>
+                </Thead>
+                <Tbody>
+                    {configMaps
+                        .filter(name => this.checkFilter(name))
+                        .map((name, idx: number) => {
+                            const configMapName = name.split("/")[0];
+                            const data = name.split("/")[1];
+                            return (
+                                <Tr key={name}>
+                                    <Td noPadding isActionCell>
+                                        <Badge>CM</Badge>
+                                    </Td>
+                                    <Td noPadding>
+                                        {configMapName}
+                                    </Td>
+                                    <Td noPadding>
+                                        <Button style={{padding: '6px'}} variant={"link"} onClick={
+                                            e => this.props.onSelect?.call(this, "configmap:" + name)}>
+                                            {data}
+                                        </Button>
+                                    </Td>
+                                </Tr>
+                            )
+                        })}
+                </Tbody>
+            </TableComposable>
+        )
+    }
+
+    getSecretsTable() {
+        const secrets = this.state.secrets;
+        return (
+            <TableComposable variant='compact' borders={false}>
+                <Thead>
+                    <Tr>
+                        <Th/>
+                        <Th key='name'>Name</Th>
+                        <Th key='data'>Data</Th>
+                    </Tr>
+                </Thead>
+                <Tbody>
+                    {secrets
+                        .filter(name => this.checkFilter(name))
+                        .map((name, idx: number) => {
+                            const configMapName = name.split("/")[0];
+                            const data = name.split("/")[1];
+                            return (
+                                <Tr key={name}>
+                                    <Td noPadding isActionCell>
+                                        <Badge>S</Badge>
+                                    </Td>
+                                    <Td noPadding>
+                                        {configMapName}
+                                    </Td>
+                                    <Td noPadding>
+                                        <Button style={{padding: '6px'}} variant={"link"} onClick={
+                                            e => this.props.onSelect?.call(this, "secret:" + name)}>
+                                            {data}
+                                        </Button>
+                                    </Td>
+                                </Tr>
+                            )
+                        })}
+                </Tbody>
+            </TableComposable>
+        )
+    }
+
+    getServicesTable() {
+        const services = this.state.services;
+        return (
+            <TableComposable variant='compact' borders={false}>
+                <Thead>
+                    <Tr>
+                        <Th/>
+                        <Th key='name'>Name</Th>
+                        {/*<Th key='hostPort'>Host:Port</Th>*/}
+                        <Th key='host'>Host</Th>
+                        <Th key='port'>Port</Th>
+                    </Tr>
+                </Thead>
+                <Tbody>
+                    {services
+                        .filter(name => this.checkFilter(name))
+                        .map((name, idx: number) => {
+                            const serviceName = name.split("|")[0];
+                            const hostPort = name.split("|")[1];
+                            const host = hostPort.split(":")[0];
+                            const port = hostPort.split(":")[1];
+                            return (
+                                <Tr key={name}>
+                                    <Td noPadding isActionCell>
+                                        <Badge>S</Badge>
+                                    </Td>
+                                    {/*<Td noPadding>*/}
+                                    {/*    {serviceName}*/}
+                                    {/*</Td>*/}
+                                    <Td noPadding>
+                                        <Button style={{padding: '6px'}} variant={"link"} onClick={
+                                            e => this.props.onSelect?.call(this, hostPort)}>
+                                            {serviceName}
+                                        </Button>
+                                    </Td>
+                                    <Td noPadding>
+                                        <Button style={{padding: '6px'}} variant={"link"} onClick={
+                                            e => this.props.onSelect?.call(this, host)}>
+                                            {host}
+                                        </Button>
+                                    </Td>
+                                    <Td noPadding>
+                                        <Button style={{padding: '6px'}} variant={"link"} onClick={
+                                            e => this.props.onSelect?.call(this, port)}>
+                                            {port}
+                                        </Button>
+                                    </Td>
+                                </Tr>
+                            )
+                        })}
+                </Tbody>
+            </TableComposable>
+        )
+    }
+
+    render() {
+        const tabIndex = this.state.tabIndex;
+        return (
+            <Modal
+                aria-label="Select from Kubernetes"
+                width={'50%'}
+                className='dsl-modal'
+                isOpen={this.props.isOpen}
+                onClose={this.props.onClose}
+                header={
+                    <Flex direction={{default: "column"}}>
+                        <FlexItem>
+                            <h3>{"Select from Kubernetes"}</h3>
+                            {this.searchInput()}
+                        </FlexItem>
+                        <FlexItem>
+                            <Tabs style={{overflow: 'hidden'}} activeKey={this.state.tabIndex} onSelect={this.selectTab}>
+                                <Tab eventKey={"configMap"} key={"configMap"} title={<TabTitleText>ConfigMaps</TabTitleText>} />
+                                <Tab eventKey={"secret"} key={"secret"} title={<TabTitleText>Secrets</TabTitleText>} />
+                                <Tab eventKey={"service"} key={"service"} title={<TabTitleText>Services</TabTitleText>} />
+                            </Tabs>
+                        </FlexItem>
+                    </Flex>
+                }
+                actions={{}}>
+                <PageSection variant={this.props.dark ? "darker" : "light"}>
+                    {this.searchInput()}
+                    {tabIndex === 'configMap' && this.getConfigMapTable()}
+                    {tabIndex === 'secret' && this.getSecretsTable()}
+                    {tabIndex === 'service' && this.getServicesTable()}
+                </PageSection>
+            </Modal>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/route/property/ObjectField.tsx b/karavan-app/src/main/webui/src/designer/route/property/ObjectField.tsx
new file mode 100644
index 0000000..a72019b
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/route/property/ObjectField.tsx
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import {DslPropertyField} from "./DslPropertyField";
+import {
+    ExpressionDefinition,
+} from "karavan-core/lib/model/CamelDefinition";
+import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import { PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
+
+interface Props {
+    property: PropertyMeta,
+    value?: CamelElement,
+    onPropertyUpdate?: (fieldId: string, value: CamelElement) => void
+    integration: Integration,
+    hideLabel?: boolean
+    dark: boolean
+}
+
+interface State {
+    value?: CamelElement,
+    selectStatus: Map<string, boolean>
+}
+
+export class ObjectField extends React.Component<Props, State> {
+    public state: State = {
+        value: this.props.value,
+        selectStatus: new Map<string, boolean>(),
+    };
+
+    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
+        if (this.props.value) {
+            const clone = CamelUtil.cloneStep(this.props.value);
+            (clone as any)[fieldId] = value;
+            this.setStep(clone)
+            this.props.onPropertyUpdate?.call(this, this.props.property.name, clone);
+        }
+    }
+
+    expressionChanged = (propertyName: string, value:ExpressionDefinition) => {
+        if (this.props.value) {
+            const clone = CamelUtil.cloneStep(this.props.value);
+            (clone as any)[propertyName] = value;
+            this.setStep(clone)
+            this.props.onPropertyUpdate?.call(this, this.props.property.name, clone);
+        }
+    }
+
+    setStep = (step?: CamelElement) => {
+        this.setState({
+            value: step,
+            selectStatus: new Map<string, boolean>()
+        });
+    }
+
+    render() {
+        const value = this.props.value;
+        return (
+                <div className="object-field">
+                    {value && CamelDefinitionApiExt.getElementProperties(value.dslName).map((property: PropertyMeta)  =>
+                        <DslPropertyField key={property.name}
+                                          hideLabel={this.props.hideLabel}
+                                          integration={this.props.integration}
+                                          property={property}
+                                          element={value}
+                                          value={value ? (value as any)[property.name] : undefined}
+                                          onExpressionChange={this.expressionChanged}
+                                          onParameterChange={(parameter, value) => this.propertyChanged(property.name, value)}
+                                          onDataFormatChange={value1 => {}}
+                                          onChange={(fieldId, value) => this.propertyChanged(property.name, value)}
+                                          dark={this.props.dark}/>
+                    )}
+                </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/templates/TemplatesDesigner.tsx b/karavan-app/src/main/webui/src/designer/templates/TemplatesDesigner.tsx
new file mode 100644
index 0000000..78c8b07
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/templates/TemplatesDesigner.tsx
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+     EmptyState, EmptyStateBody, EmptyStateIcon,
+    PageSection, Title
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import CubesIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon';
+
+interface Props {
+    onSave?: (integration: Integration, propertyOnly: boolean) => void
+    integration: Integration
+    dark: boolean
+}
+
+interface State {
+    integration: Integration
+    selectedStep?: CamelElement
+    key: string
+    propertyOnly: boolean
+}
+
+export class TemplatesDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: this.props.integration,
+        key: "",
+        propertyOnly: false
+    };
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+        if (prevState.key !== this.state.key) {
+            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
+        }
+    }
+
+    onIntegrationUpdate = (i: Integration) => {
+        this.setState({integration: i, key: Math.random().toString()});
+    }
+
+    render() {
+        return (
+            <PageSection className="templates-page" isFilled padding={{default: 'noPadding'}}>
+                <div className="templates-page-columns">
+                    <EmptyState>
+                        <EmptyStateIcon icon={CubesIcon} />
+                        <Title headingLevel="h4" size="lg">
+                            Templates
+                        </Title>
+                        <EmptyStateBody>
+                            Templates not implemented yet
+                        </EmptyStateBody>
+                    </EmptyState>
+                </div>
+            </PageSection>
+        );
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx b/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
new file mode 100644
index 0000000..3083c36
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
@@ -0,0 +1,524 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {KameletApi} from "karavan-core/lib/api/KameletApi";
+import {KameletModel} from "karavan-core/lib/model/KameletModels";
+import {DslMetaModel} from "./DslMetaModel";
+import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
+import {CamelMetadataApi} from "karavan-core/lib/model/CamelMetadata";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {NamedBeanDefinition, RouteDefinition, SagaDefinition, ToDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {AggregateIcon, ChoiceIcon, FilterIcon, SagaIcon, SortIcon, SplitIcon, TransformIcon} from "./KaravanIcons";
+import React from "react";
+
+const StepElements: string[] = [
+    "AggregateDefinition",
+    "BeanDefinition",
+    "ChoiceDefinition",
+    "CircuitBreakerDefinition",
+    "ClaimCheckDefinition",
+    "ConvertBodyDefinition",
+    "DynamicRouterDefinition",
+    "EnrichDefinition",
+    // "ErrorHandlerBuilderDeserializer",
+    // "ErrorHandlerDefinition",
+    "FilterDefinition",
+    "LogDefinition",
+    "LoopDefinition",
+    "MarshalDefinition",
+    "MulticastDefinition",
+    "PollEnrichDefinition",
+    "ProcessDefinition",
+    "RecipientListDefinition",
+    "RemoveHeaderDefinition",
+    "RemoveHeadersDefinition",
+    "RemovePropertyDefinition",
+    "ResequenceDefinition",
+    "SagaDefinition",
+    "SetBodyDefinition",
+    "SetHeaderDefinition",
+    "SetPropertyDefinition",
+    "SortDefinition",
+    "SplitDefinition",
+    "StepDefinition",
+    "ThreadsDefinition",
+    "ThrottleDefinition",
+    "ThrowExceptionDefinition",
+    "ToDynamicDefinition",
+    "TransformDefinition",
+    "TransactedDefinition",
+    "TryDefinition",
+    "UnmarshalDefinition",
+    "ValidateDefinition",
+    "WireTapDefinition"
+];
+
+export const camelIcon =
+    "data:image/svg+xml,%3Csvg viewBox='0 0 130.21 130.01' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='333.48' x2='477' y1='702.6' y2='563.73' gradientTransform='translate(94.038 276.06) scale(.99206)' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23F69923' offset='0'/%3E%3Cstop stop-color='%23F79A23' offset='.11'/%3E%3Cstop stop-color='%23E97826' offset='.945'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' x1='333.48' x2='477' y1='702.6' y2='563 [...]
+
+export const externalIcon =
+    "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32px' height='32px' viewBox='0 0 32 32' id='icon'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Efog%3C/title%3E%3Cpath d='M25.8289,13.1155A10.02,10.02,0,0,0,16,5.0005V7a8.0233,8.0233,0,0,1,7.8649,6.4934l.2591,1.346,1.3488.2441A5.5019,5.5019,0,0,1,24.5076,26H16v2h8.5076a7.5019,7.5019,0,0,0,1.3213-14.8845Z'/%3E%3Crect x='8' y='24' width='6' height='2'/%3E%3Crect x='4' y='24' width='2'  [...]
+
+export class RouteToCreate {
+    componentName: string = ''
+    name: string = ''
+
+    constructor(componentName: string, name: string) {
+        this.componentName = componentName;
+        this.name = name;
+    }
+}
+
+export class CamelUi {
+
+    static getSelectorModelTypes = (parentDsl: string | undefined, showSteps: boolean = true, filter:string|undefined = undefined): [string, number][] => {
+        const navs =  CamelUi.getSelectorModelsForParent(parentDsl, showSteps).map(dsl => dsl.navigation.split(","))
+            .reduce((accumulator, value) => accumulator.concat(value), [])
+            .filter((nav, i, arr) => arr.findIndex(l => l === nav) === i)
+            .filter((nav, i, arr) => ![ 'dataformat'].includes(nav));
+        const connectorNavs = ['routing', "transformation", "error", "configuration", "endpoint", "kamelet", "component"];
+        const eipLabels = connectorNavs.filter(n => navs.includes(n));
+        return eipLabels.map(label => [label, this.getSelectorModelsForParentFiltered(parentDsl, label, true)
+            .filter((dsl: DslMetaModel) => filter === undefined ? true : CamelUi.checkFilter(dsl, filter)).length]);
+    }
+
+    static checkFilter = (dsl: DslMetaModel, filter:string|undefined = undefined): boolean => {
+        if (filter !== undefined && filter !== "") {
+            return dsl.title.toLowerCase().includes(filter.toLowerCase())
+                || dsl.description.toLowerCase().includes(filter.toLowerCase());
+        } else {
+            return true;
+        }
+    }
+
+    static dslHasSteps = (className: string): boolean => {
+        return CamelDefinitionApiExt.getElementChildrenDefinition(className).filter(c => c.name === 'steps').length === 1;
+    }
+
+
+    static getSelectorRestMethodModels = (): DslMetaModel[] => {
+        return ['GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].map(method => this.getDslMetaModel(method));
+    }
+
+    static getSelectorModelsForParentFiltered = (parentDsl: string | undefined, navigation: string,  showSteps: boolean = true): DslMetaModel[] => {
+        return CamelUi.getSelectorModelsForParent(parentDsl, showSteps)
+            .filter(dsl => dsl.navigation.includes(navigation));
+            
+        }
+    static getSelectorModelsForParent = (parentDsl: string | undefined, showSteps: boolean = true): DslMetaModel[] => {
+        const result: DslMetaModel[] = [];
+        if (!parentDsl){
+            result.push(...CamelUi.getComponentsDslMetaModel("consumer"));
+            result.push(...CamelUi.getKameletDslMetaModel("source"));
+        } else {
+            if (showSteps) {
+                if (parentDsl && CamelDefinitionApiExt.getElementChildrenDefinition(parentDsl).filter(child => child.name === 'steps').length > 0) {
+                    StepElements.forEach(se => {
+                        result.push(CamelUi.getDslMetaModel(se));
+                    })
+                }
+                result.push(...CamelUi.getComponentsDslMetaModel("producer"));
+                result.push(...CamelUi.getKameletDslMetaModel("action"));
+                result.push(...CamelUi.getKameletDslMetaModel("sink"));
+            } else {
+                const children = CamelDefinitionApiExt.getElementChildrenDefinition(parentDsl).filter(child => child.name !== 'steps')
+                children.filter(child => {
+                    const cc = CamelDefinitionApiExt.getElementChildrenDefinition(child.className);
+                    return child.name === 'steps' || cc.filter(c => c.multiple).length > 0;
+                })
+                    .forEach(child => result.push(CamelUi.getDslMetaModel(child.className)));
+            }
+        }
+        return result.length > 1 ? result.sort((a, b) => (a.title?.toLowerCase() > b.title?.toLowerCase() ? 1 : -1)) : [];
+    }
+
+    static getDslMetaModel = (className: string): DslMetaModel => {
+        const el = CamelMetadataApi.getCamelModelMetadataByClassName(className);
+        return  new DslMetaModel({dsl: className, name: el?.name, title: el?.title, description: el?.description, labels: el?.labels, navigation: el?.labels, type: "DSL"})
+    }
+
+    static getComponentsDslMetaModel = (type: 'consumer' | "producer"): DslMetaModel[] => {
+        return ComponentApi.getComponents().filter((c) => type === 'consumer' ? !c.component.producerOnly : !c.component.consumerOnly)
+            .map((c) =>
+                new DslMetaModel({
+                    dsl: type === 'consumer' ? "FromDefinition" : "ToDefinition",
+                    uri: c.component.name,
+                    navigation: "component",
+                    labels: c.component.label,
+                    type: type === 'consumer' ? 'consumer' : 'producer',
+                    title: c.component.title,
+                    description: c.component.description,
+                    version: c.component.version,
+                }));
+    }
+
+    static getKameletDslMetaModel = (type: 'source' | "sink" | "action"): DslMetaModel[] => {
+        return KameletApi.getKamelets().filter((k) => k.metadata.labels["camel.apache.org/kamelet.type"] === type)
+            .map((k) => {
+                const descriptionLines = k.description().split("\n");
+                const description = descriptionLines.at(0);
+                return new DslMetaModel({
+                    dsl: type === 'source' ? "FromDefinition" : "ToDefinition",
+                    uri: "kamelet:" + k.metadata.name,
+                    labels: k.type(),
+                    navigation: "kamelet",
+                    type: k.type(),
+                    name: k.metadata.name,
+                    title: k.title(),
+                    description: description,
+                    version: k.version(),
+                    supportLevel: k.metadata.annotations["camel.apache.org/kamelet.support.level"],
+                })
+            });
+    }
+
+    static nameFromTitle = (title: string): string => {
+        return title.replace(/[^a-z0-9+]+/gi, "-").toLowerCase();
+    }
+
+    static javaNameFromTitle = (title: string): string => {
+        const name = CamelUi.nameFromTitle(title);
+        return name.split("-").map(v => CamelUtil.capitalizeName(v)).join('');
+    }
+
+    static titleFromName = (name?: string) => {
+        name = name ? (name.substring(0, name.lastIndexOf('.')) || name) : undefined;
+        return name
+            ? name
+                .replace(".yaml", "")
+                .split("-")
+                .map((value) => CamelUtil.capitalizeName(value))
+                .reduce(
+                    (previousValue, currentValue) => previousValue + " " + currentValue
+                )
+            : name;
+    }
+
+    static isActionKamelet = (element: CamelElement): boolean => {
+        const kamelet = CamelUtil.getKamelet(element);
+        if (kamelet) return kamelet.type() === 'action'
+        else return false;
+    }
+
+    static hasInternalUri = (element: CamelElement): boolean => {
+        return this.hasDirectUri(element) || this.hasSedaUri(element);
+    }
+
+    static hasDirectUri = (element: CamelElement): boolean => {
+        return this.hasUriStartWith(element,'direct');
+    }
+
+    static hasSedaUri = (element: CamelElement): boolean => {
+        return this.hasUriStartWith(element,'seda');
+    }
+
+    static hasUriStartWith = (element: CamelElement, text: string): boolean => {
+        if ((element as any).uri && typeof (element as any).uri === 'string') {
+            return (element as any).uri.startsWith(text);
+        } else if (element.dslName === 'SagaDefinition'){
+            const completion = (element as SagaDefinition).completion || '';
+            const compensation = (element as SagaDefinition).compensation || '';
+            return completion.startsWith(text) || compensation.startsWith(text);
+        } else {
+            return false;
+        }
+    }
+
+    static getInternalRouteUris = (integration: Integration, componentName: string, showComponentName: boolean = true): string[] => {
+        const result:string[] = [];
+        integration.spec.flows?.filter(f => f.dslName === 'RouteDefinition')
+            .filter((r: RouteDefinition) => r.from.uri.startsWith(componentName))
+            .forEach((r: RouteDefinition) => {
+                if (showComponentName) result.push(r.from.uri)
+                else result.push(r.from.uri.replace(componentName+":", ""));
+            });
+        return result;
+    }
+
+    static getElementTitle = (element: CamelElement): string => {
+        if (element.dslName === 'RouteDefinition') {
+            const routeId = (element as RouteDefinition).id
+            return routeId ? routeId : CamelUtil.capitalizeName((element as any).stepName);
+        } else if (['ToDefinition', 'ToDynamicDefinition', 'FromDefinition', 'KameletDefinition'].includes(element.dslName) && (element as any).uri) {
+            const uri = (element as any).uri;
+            const kameletTitle = uri && uri.startsWith("kamelet:") ? KameletApi.findKameletByUri(uri)?.title() : undefined;
+            return kameletTitle ? kameletTitle : CamelUtil.capitalizeName(ComponentApi.getComponentTitleFromUri(uri) || '');
+        } else {
+            const title = CamelMetadataApi.getCamelModelMetadataByClassName(element.dslName);
+            return title ? title.title : CamelUtil.capitalizeName((element as any).stepName);
+        }
+    }
+
+    static getTitle = (element: CamelElement): string => {
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
+        if (k) {
+            return k.title();
+        } else if (element.dslName === 'RouteDefinition') {
+            const routeId = (element as RouteDefinition).id
+            return routeId ? routeId : CamelUtil.capitalizeName((element as any).stepName);
+        } else if ((element as any).uri && element.dslName === 'ToDefinition') {
+            const uri = (element as any).uri
+            return ComponentApi.getComponentTitleFromUri(uri) || '';
+        } else {
+            const title = CamelMetadataApi.getCamelModelMetadataByClassName(element.dslName);
+            return title ? title.title : CamelUtil.capitalizeName((element as any).stepName);
+        }
+    }
+
+    static getDescription = (element: CamelElement): string => {
+        const kamelet: KameletModel | undefined = CamelUtil.getKamelet(element);
+        if (kamelet) {
+            return kamelet.spec.definition.description;
+        } else if ((element as any).uri && element.dslName === 'ToDefinition') {
+            const uri = (element as any).uri
+            return ComponentApi.getComponentDescriptionFromUri(uri) || '';
+        } else {
+            const description = CamelMetadataApi.getCamelModelMetadataByClassName(element.dslName)?.description;
+            return description ? description : CamelUi.getTitle(element);
+        }
+    }
+
+    static getOutgoingTitle = (element: CamelElement): string => {
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
+        if (k) {
+            return k.title();
+        } else if (element.dslName === 'RouteDefinition') {
+            const routeId = (element as RouteDefinition).id
+            return routeId ? routeId : CamelUtil.capitalizeName((element as any).stepName);
+        } else if ((element as any).uri) {
+            const uri = (element as any).uri
+            return ComponentApi.getComponentTitleFromUri(uri) || uri;
+        } else {
+            return "";
+        }
+    }
+
+    static isShowExpressionTooltip = (element: CamelElement): boolean => {
+        if (element.hasOwnProperty("expression")){
+            const exp = CamelDefinitionApiExt.getExpressionValue((element as any).expression);
+            return (exp !== undefined && (exp as any)?.expression?.trim().length > 0);
+        }
+        return false;
+    }
+
+    static isShowUriTooltip = (element: CamelElement): boolean => {
+        const uri: string = (element as any).uri;
+        if (uri !== undefined && !uri.startsWith("kamelet")){
+            return ComponentApi.getComponentNameFromUri(uri) !== uri;
+        }
+        return false;
+    }
+
+    static getExpressionTooltip = (element: CamelElement): string => {
+        const e = (element as any).expression;
+        const language = CamelDefinitionApiExt.getExpressionLanguageName(e) || '';
+        const value = CamelDefinitionApiExt.getExpressionValue(e) || '';
+        return language.concat(": ", (value as any)?.expression);
+    }
+
+    static getUriTooltip = (element: CamelElement): string => {
+        return (element as any).uri;
+    }
+
+    static getKameletIconByUri = (uri: string | undefined): string => {
+        return uri ? KameletApi.findKameletByUri(uri)?.icon() || "" : "";
+    }
+
+    static getKameletIconByName = (name: string | undefined): string => {
+        return name ? KameletApi.findKameletByName(name)?.icon() || "" : "";
+    }
+
+    static getIconSrcForName = (dslName: string): string => {
+        switch (dslName) {
+            case "FilterDefinition":
+                return "data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-prefix='fas' data-icon='filter' class='svg-inline--fa fa-filter fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z'%3E%3C/path%3E%3C/svg%3E";
+            case "OtherwiseDefinition":
+                return "data:image/svg+xml,%0A%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E .cls-1 %7B fill: none; %7D %3C/style%3E%3C/defs%3E%3Crect x='12' y='24' width='9' height='2'/%3E%3Crect x='13' y='28' width='6' height='2'/%3E%3Cpath d='M8.7832,18.9746l1.4177-1.418A6.9206,6.9206,0,0,1,8,12,7.99,7.99,0,0,1,21.5273,6.2305l1.4136-1.4136A9.9884,9.9884,0,0,0,6,12,8.9411,8.9411,0,0,0,8.7 [...]
+            case "ChoiceDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16px' height='16px' viewBox='0 0 16 16' fill='currentColor' class='bi bi-signpost-split-fill'%3E%3Cpath d='M7 16h2V6h5a1 1 0 0 0 .8-.4l.975-1.3a.5.5 0 0 0 0-.6L14.8 2.4A1 1 0 0 0 14 2H9v-.586a1 1 0 0 0-2 0V7H2a1 1 0 0 0-.8.4L.225 8.7a.5.5 0 0 0 0 .6l.975 1.3a1 1 0 0 0 .8.4h5v5z'/%3E%3C/svg%3E";
+            case "WhenDefinition":
+                return "data:image/svg+xml,%0A%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='icon' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Eidea%3C/title%3E%3Crect x='11' y='24' width='10' height='2'/%3E%3Crect x='13' y='28' width='6' height='2'/%3E%3Cpath d='M16,2A10,10,0,0,0,6,12a9.19,9.19,0,0,0,3.46,7.62c1,.93,1.54,1.46,1.54,2.38h2c0-1.84-1.11-2.87-2.19-3.86A7.2,7.2,0,0,1,8,12a8,8,0,0,1,16,0,7.2,7.2,0, [...]
+            case "AggregateDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='icon' x='0px' y='0px' width='32px' height='32px' viewBox='0 0 32 32' style='enable-background:new 0 0 32 32;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:none;%7D%0A%3C/style%3E%3Cpath d='M16,4c6.6,0,12,5.4,12,12s-5.4,12-12,12S4,22.6,4,16S9.4,4,16,4 M16,2C8.3,2,2,8.3,2,16s6.3,14,14,14s14-6.3,14-14 S23.7,2,16,2z'/%3E%3Cpolygon [...]
+            case "SplitDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32px' height='32px' viewBox='0 0 32 32' id='icon'%3E%3Cdefs%3E%3Cstyle%3E .cls-1 %7B fill: none; %7D %3C/style%3E%3C/defs%3E%3Ctitle%3Esplit%3C/title%3E%3Crect x='15' y='20' width='2' height='4'/%3E%3Crect x='15' y='14' width='2' height='4'/%3E%3Crect x='15' y='8' width='2' height='4'/%3E%3Cpath d='M28,16A12.01,12.01,0,0,0,17,4.0508V2H15V4.0508a11.99,11.99,0,0,0,0,23.8984V30h2V27.9492A12.01,12.01, [...]
+            case "SortDefinition":
+                return "data:image/svg+xml,%0A%3Csvg aria-hidden='true' focusable='false' data-prefix='fas' data-icon='sort-amount-down' class='svg-inline--fa fa-sort-amount-down fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M304 416h-64a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-128-64h-48V48a16 16 0 0 0-16-16H80a16 16 0 0 0-16 16v304H16c-14.19 0-21.37 17.24-11.29 27.31l80 96a16 16 0 [...]
+            case "ResequenceDefinition":
+                return "data:image/svg+xml,%0A%3Csvg aria-hidden='true' focusable='false' data-prefix='fas' data-icon='sort-numeric-down' class='svg-inline--fa fa-sort-numeric-down fa-w-14' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M304 96h16v64h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16h-16V48a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 304 96zm26.15 162.91a79 79 0 0 [...]
+            case "RecipientListDefinition":
+                return "data:image/svg+xml,%0A%3Csvg aria-hidden='true' focusable='false' data-prefix='fas' data-icon='list-ul' class='svg-inline--fa fa-list-ul fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M48 48a48 48 0 1 0 48 48 48 48 0 0 0-48-48zm0 160a48 48 0 1 0 48 48 48 48 0 0 0-48-48zm0 160a48 48 0 1 0 48 48 48 48 0 0 0-48-48zm448 16H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16- [...]
+            case "LoopDefinition":
+                return "data:image/svg+xml,%3Csvg version='1.1' id='Capa_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 20.298 20.298' style='enable-background:new 0 0 20.298 20.298;' xml:space='preserve'%3E%3Cg%3E%3Cg%3E%3Cg%3E%3Cpath style='fill:%23030104;' d='M0.952,11.102c0-0.264,0.213-0.474,0.475-0.474h2.421c0.262,0,0.475,0.21,0.475,0.474 c0,3.211,2.615,5.826,5.827,5.826s5.827-2.615,5.827-5.826c0-3.214-2.614-5.826-5.827- [...]
+            case "MulticastDefinition":
+                return "data:image/svg+xml,%3Csvg width='433' height='366' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg' enable-background='new 0 0 378.06 378.06' version='1.1' xml:space='preserve'%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cg id='svg_1'%3E%3Cpolygon id='svg_4' points='222.36802673339844,225.9189910888672 107.00502014160156,225.9189910888672 107.00502014160156,284.5920104980469 222.36802673339844,284.5920104980469 222.3680267333984 [...]
+            case "TryDefinition":
+                return "data:image/svg+xml,%3Csvg width='160' height='412' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg' enable-background='new 0 0 436.346 436.346' version='1.1' xml:space='preserve'%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cg id='svg_1'%3E%3Cpolygon id='svg_2' points='0,133.14898681640625 80.10099792480469,256.20098876953125 160.2010040283203,133.14898681640625 113.96000671386719,133.14898681640625 113.96000671386719,0 46.241012 [...]
+            case "CatchDefinition":
+                return "data:image/svg+xml,%3Csvg width='232' height='477' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg' enable-background='new 0 0 476.457 476.457' version='1.1' xml:space='preserve'%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cg id='svg_1' transform='rotate(-90 117.228 239.228)'%3E%3Cpath d='m188.688,232.118c-33.574,0 -60.888,27.314 -60.888,60.889c0,33.573 27.314,60.888 60.888,60.888s60.888,-27.314 60.888,-60.888c0,-33.575 -27.314, [...]
+            case "FinallyDefinition":
+                return "data:image/svg+xml,%3Csvg width='161' height='423' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg' enable-background='new 0 0 436.346 436.346' version='1.1' xml:space='preserve'%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cg id='svg_1'%3E%3Cpolygon id='svg_2' points='0.9282517433166504,300.3690004348755 81.02924966812134,423.4210023880005 161.1292634010315,300.3690004348755 114.88825845718384,300.3690004348755 114.8882584571838 [...]
+            case "LogDefinition":
+                return "data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%0A%3E%3Cpath d='M8.01562 6.98193C7.46334 6.98193 7.01562 7.43285 7.01562 7.98513C7.01562 8.53742 7.46334 8.98833 8.01563 8.98833H15.9659C16.5182 8.98833 16.9659 8.53742 16.9659 7.98513C16.9659 7.43285 16.5182 6.98193 15.9659 6.98193H8.01562Z' fill='currentColor' /%3E%3Cpath d='M7.01562 12C7.01562 11.4477 7.46334 10.9968 8.01562 10.9968H15.9659C16.518 [...]
+            case "CircuitBreakerDefinition":
+                return "data:image/svg+xml,%3Csvg width='298' height='298' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg' enable-background='new 0 0 298 298' version='1.1'%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cg id='svg_1'%3E%3Cpath d='m169.34692,382.94627l0,-107.7738c18.40098,-6.01731 31.65307,-22.53006 31.65307,-41.92199c0,-24.464 -21.08286,-44.36477 -47.00001,-44.36477c-25.91715,0 -47.00001,19.90077 -47.00001,44.36477c0,19.39193 13.25208,35 [...]
+            case "OnFallbackDefinition":
+                return "data:image/svg+xml,%3Csvg width='24px' height='24px' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.2685 11.3076C14.9169 11.0248 14.5923 10.7795 14.2848 10.5693C13.3451 9.92712 12.672 9.68977 12 9.68977C11.328 9.68977 10.6549 9.92712 9.71521 10.5693C8.75213 11.2275 7.62138 12.2301 6.02073 13.6529L3.66436 15.7474C3.25158 16.1143 2.61951 16.0771 2.25259 15.6644C1.88567 15.2516 1.92285 14.6195 2.33564 14.2526L4.74407 12.1118C6.280 [...]
+            case "ThreadsDefinition":
+                return "data:image/svg+xml,%0A%3Csvg width='256px' height='256px' viewBox='0 0 256 256' id='Flat' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M30.62988,79.49609a11.99872,11.99872,0,0,1,1.874-16.86621c1.23242-.98633,12.56543-9.7666,30.58593-14.27148,16.94727-4.2334,43.1211-5.30762,71.56641,13.65722,40.23389,26.82129,73.51563.87891,73.84766.61426a11.9996,11.9996,0,0,1,14.99218,18.74024c-1.23242.98633-12.56543,9.7666-30.58593,14.27148a85.50742,85.50742,0,0,1-20.71485,2.5 [...]
+            case "ThrottleDefinition":
+                return "data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 360 360' style='enable-background:new 0 0 360 360;' xml:space='preserve'%3E%3Cg id='XMLID_12_'%3E%3Cpath id='XMLID_13_' d='M102.342,246.475C99.541,242.42,94.928,240,90,240s-9.541,2.42-12.342,6.475 c-0.32,0.463-7.925,11.497-15.633,24.785C46.765,297.566,45,308.822,45,315c0,24.813,20.187,45,45,45s45-20.187,45- [...]
+            case "WireTapDefinition":
+                return "data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m8,8c1.86658,0 3.4346,1.27853 3.8759,3.0076l0.06156,-0.00568l0,0l3.06254,-0.00192c0.5523,0 1,0.4477 1,1c0,0.5523 -0.4477,1 -1,1l-3,0c-0.042,0 -0.0834,-0.0026 -0.1241,-0.0076c-0.4413,1.7291 -2.00932,3.0076 -3.8759,3.0076c-1.86658,0 -3.43455,-1.2785 -3.87594,-3.0076c-0.04064,0.005 - [...]
+            case "ToDynamicDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24.08924,9.30736l-1.67194,1.57734l4.23986,3.99986l-5.47865,0a5.92883,5.59337 0 0 0 -4.60981,-4.34899l0,-10.15173l-2.36467,0l0,10.15173a5.91168,5.5772 0 0 0 0,10.92886l0,10.15173l2.36467,0l0,-10.15173a5.92883,5. [...]
+            case "RemoveHeaderDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...]
+            case "RemovePropertyDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...]
+            case "RemoveHeadersDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpolygon id='svg_2' points='32.12467002868652,6.015733242034912 32.12467002868652,4.021692276000977 27.047643661499023,4.021692276000977 27.047643661499023,-1.0553351640701294 25.05360221862793,-1.0553350448608398 25.053 [...]
+            case "SetHeaderDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...]
+            case "SetPropertyDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...]
+            case "SetBodyDefinition":
+                return "data:image/svg+xml,%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='icon' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E .cls-1 %7B fill: none; %7D %3C/style%3E%3C/defs%3E%3Cpolygon points='30 24 26 24 26 20 24 20 24 24 20 24 20 26 24 26 24 30 26 30 26 26 30 26 30 24'/%3E%3Cpath d='M16,28H8V4h8v6a2.0058,2.0058,0,0,0,2,2h6v4h2V10a.9092.9092,0,0,0-.3-.7l-7-7A.9087.9087,0,0,0,18,2H8A2.0058,2.0058,0,0,0,6,4V28a2.0058,2.0058,0,0,0,2,2h8ZM18,4.4,23. [...]
+            case "MarshalDefinition":
+                return "data:image/svg+xml,%0A%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='icon' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Edocument--export%3C/title%3E%3Cpolygon points='13 21 26.17 21 23.59 23.59 25 25 30 20 25 15 23.59 16.41 26.17 19 13 19 13 21'/%3E%3Cpath d='M22,14V10a1,1,0,0,0-.29-.71l-7-7A1,1,0,0,0,14,2H4A2,2,0,0,0,2,4V28a2,2,0,0,0,2,2H20a2,2,0,0,0,2-2V26H20v2H4V4h8v6a2,2,0,0,0,2,2h [...]
+            case "UnmarshalDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32px' height='32px' viewBox='0 0 32 32' id='icon'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Edocument--import%3C/title%3E%3Cpolygon points='28 19 14.83 19 17.41 16.41 16 15 11 20 16 25 17.41 23.59 14.83 21 28 21 28 19'/%3E%3Cpath d='M24,14V10a1,1,0,0,0-.29-.71l-7-7A1,1,0,0,0,16,2H6A2,2,0,0,0,4,4V28a2,2,0,0,0,2,2H22a2,2,0,0,0,2-2V26H22v2H6V4h8v6a2,2,0,0,0,2,2h6v2 [...]
+            case "ValidateDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m16,28l-8,0l0,-24l8,0l0,6a2.0058,2.0058 0 0 0 2,2l6,0l0,4l2,0l0,-6a0.9092,0.9092 0 0 0 -0.3,-0.7l-7,-7a0.9087,0.9087 0 0 0 -0.7,-0.3l-10,0a2.0058,2.0058 0 0 0 -2,2l0,24a2.0058,2.0058 0 0 0 2,2l8,0l0,-2zm2,-23.6l [...]
+            case "ConvertBodyDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m16,28l-8,0l0,-24l8,0l0,6a2.0058,2.0058 0 0 0 2,2l6,0l0,4l2,0l0,-6a0.9092,0.9092 0 0 0 -0.3,-0.7l-7,-7a0.9087,0.9087 0 0 0 -0.7,-0.3l-10,0a2.0058,2.0058 0 0 0 -2,2l0,24a2.0058,2.0058 0 0 0 2,2l8,0l0,-2zm2,-23.6l [...]
+            case "TransformDefinition":
+                return "data:image/svg+xml,%0A%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='icon' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Escript%3C/title%3E%3Cpolygon points='18.83 26 21.41 23.42 20 22 16 26 20 30 21.42 28.59 18.83 26'/%3E%3Cpolygon points='27.17 26 24.59 28.58 26 30 30 26 26 22 24.58 23.41 27.17 26'/%3E%3Cpath d='M14,28H8V4h8v6a2.0058,2.0058,0,0,0,2,2h6v6h2V10a.9092.9092,0,0,0-.3-.7l-7 [...]
+            case "EnrichDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpolygon id='svg_1' points='4,20 4,22 8.586000442504883,22 2,28.586000442504883 3.4140000343322754,30 10,23.413999557495117 10,28 12,28 12,20 4,20 '/%3E%3Cpath d='m25.7,9.3l-7,-7a0.9087,0.9087 0 0 0 -0.7,-0.3l-10,0a2.005 [...]
+            case "PollEnrichDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpolygon id='svg_1' points='4,20 4,22 8.586000442504883,22 2,28.586000442504883 3.4140000343322754,30 10,23.413999557495117 10,28 12,28 12,20 4,20 '/%3E%3Cpath d='m25.7,9.3l-7,-7a0.9087,0.9087 0 0 0 -0.7,-0.3l-10,0a2.005 [...]
+            case "TransactedDefinition":
+                return "data:image/svg+xml,%0A%3Csvg width='24px' height='24px' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='M2,7 L20,7 M16,2 L21,7 L16,12 M22,17 L4,17 M8,12 L3,17 L8,22'/%3E%3C/svg%3E%0A";
+            case "SagaDefinition":
+                return "data:image/svg+xml,%0A%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='icon' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Eexpand-categories%3C/title%3E%3Crect x='20' y='26' width='6' height='2'/%3E%3Crect x='20' y='18' width='8' height='2'/%3E%3Crect x='20' y='10' width='10' height='2'/%3E%3Crect x='15' y='4' width='2' height='24'/%3E%3Cpolygon points='10.586 3.959 7 7.249 3.412 3.958 2  [...]
+            case "FromDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24px' height='24px' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12.6577283,22.7532553 L12,23.3275712 L11.3422717,22.7532553 C5.81130786,17.9237218 3,13.70676 3,10 C3,4.7506636 7.09705254,1 12,1 C16.9029475,1 21,4.7506636 21,10 C21,13.70676 18.1886921,17.9237218 12.6577283,22.7532553 Z M5,10 C5,12.8492324 7.30661202,16.4335466 12,20.6634039 C16.693388,16.4335466 19,12.8492324 19,10 C19,5. [...]
+            case "ToDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24.08924,9.30736l-1.67194,1.57734l4.23986,3.99986l-5.47865,0a5.92883,5.59337 0 0 0 -4.60981,-4.34899l0,-10.15173l-2.36467,0l0,10.15173a5.91168,5.5772 0 0 0 0,10.92886l0,10.15173l2.36467,0l0,-10.15173a5.92883,5. [...]
+            case "SwitchDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Ctitle%3Emilestone%3C/title%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24.5857,6.5859a1.9865,1.9865 0 0 0 -1.4143,-0.5859l-7.1714,0l0,-4l-2,0l0,4l-8,0a2.0025,2.0025 0 0 0 -2,2l0,6a2.0025,2.0025 0 0 0 2,2l8,0l0,14l2,0l0,-14l7.1714,0a1.9865,1.9865 0 0 [...]
+            case "KameletDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Capa_1' x='0px' y='0px' width='235.506px' height='235.506px' viewBox='0 0 235.506 235.506' style='enable-background:new 0 0 235.506 235.506;' xml:space='preserve'%3E%3Cg%3E%3Cpath d='M234.099,29.368c-3.025-4.861-10.303-7.123-22.915-7.123c-13.492,0-28.304,2.661-28.625,2.733 c-20.453,2.098-27.254,26.675-32.736,46.436c-1.924,6.969-3.755,13.549-5.8 [...]
+            case "DynamicRouterDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Einsert%3C/title%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Crect class='cls-1' data-name='&lt;Transparent Rectangle&gt;' height='32' id='_Transparent_Rectangle_' width='31.94228' x='0' y='0'/%3E%3Cg id='svg_5' transform='matrix(1 0 0 1 0 0) rotate(180 16 [...]
+            case "ErrorHandlerBuilderRef":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='top-icon' width='36px' height='36px' viewBox='0 0 36 36' version='1.1' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle class='clr-i-outline clr-i-outline-path-1' cx='18' cy='26.06' r='1.33'%3E%3C/circle%3E%3Cpath class='clr-i-outline clr-i-outline-path-2' d='M18,22.61a1,1,0,0,1-1-1v-12a1,1,0,1,1,2,0v12A1,1,0,0,1,18,22.61Z'%3E%3C/path%3E%3Cpath class='clr-i-outline clr-i-outline-path-3' d='M18,34A1 [...]
+            case "ThrowExceptionDefinition":
+                return "data:image/svg+xml,%3Csvg version='1.1' id='icon' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='32px' height='32px' viewBox='0 0 32 32' style='enable-background:new 0 0 32 32;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:none;%7D .st1%7Bopacity:0;fill-opacity:0;%7D%0A%3C/style%3E%3Crect id='_Transparent_Rectangle_' class='st0' width='32' height='32'/%3E%3Cpath d='M16,2C8.3,2,2,8.3,2,16s6.3,1 [...]
+            case "OnExceptionDefinition":
+                return "data:image/svg+xml,%3Csvg version='1.1' id='icon' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='32px' height='32px' viewBox='0 0 32 32' style='enable-background:new 0 0 32 32;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:none;%7D%0A%3C/style%3E%3Ctitle%3Echeckmark%3C/title%3E%3Cpath d='M16,2C8.2,2,2,8.2,2,16s6.2,14,14,14s14-6.2,14-14S23.8,2,16,2z M16,28C9.4,28,4,22.6,4,16S9.4,4,16,4s12,5.4,1 [...]
+            case "StepDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32px' height='32px' viewBox='0 0 32 32' id='icon'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Ecollapse-all%3C/title%3E%3Cpath d='M30,15H28V7H13V5H28a2.0023,2.0023,0,0,1,2,2Z'/%3E%3Cpath d='M25,20H23V12H8V10H23a2.0023,2.0023,0,0,1,2,2Z'/%3E%3Cpath d='M18,27H4a2.0023,2.0023,0,0,1-2-2V17a2.0023,2.0023,0,0,1,2-2H18a2.0023,2.0023,0,0,1,2,2v8A2.0023,2.0023,0,0,1,18,27Z [...]
+            case "BeanDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1' x='0px' y='0px' viewBox='0 0 305.001 305.001' style='enable-background:new 0 0 305.001 305.001;' xml:space='preserve'%3E%3Cg id='XMLID_7_'%3E%3Cpath id='XMLID_8_' d='M150.99,56.513c-14.093,9.912-30.066,21.147-38.624,39.734c-14.865,32.426,30.418,67.798,32.353,69.288 c0.45,0.347,0.988,0.519,1.525,0.519c0.57,0,1.141-0.195,1.605-0.583c0.89 [...]
+            case "ProcessDefinition":
+                return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Capa_1' x='0px' y='0px' width='235.506px' height='235.506px' viewBox='0 0 235.506 235.506' style='enable-background:new 0 0 235.506 235.506;' xml:space='preserve'%3E%3Cg%3E%3Cpath d='M234.099,29.368c-3.025-4.861-10.303-7.123-22.915-7.123c-13.492,0-28.304,2.661-28.625,2.733 c-20.453,2.098-27.254,26.675-32.736,46.436c-1.924,6.969-3.755,13.549-5.8 [...]
+            default:
+                return camelIcon;
+        }
+    }
+
+    static getIconForDsl = (dsl: DslMetaModel):JSX.Element => {
+        if (dsl.dsl && (dsl.dsl === "KameletDefinition" || dsl.navigation === 'kamelet')) {
+            return this.getIconFromSource(CamelUi.getKameletIconByName(dsl.name));
+        } else if ((dsl.dsl && dsl.dsl === "FromDefinition")
+            && dsl.uri?.startsWith("kamelet")) {
+            return this.getIconFromSource(CamelUi.getKameletIconByUri(dsl.uri));
+        } else if (dsl.navigation === 'component' ){
+            return this.getIconFromSource(camelIcon);
+        } else {
+            return CamelUi.getIconForDslName(dsl.dsl);
+        }
+    }
+
+    static getIconForElement = (element: CamelElement):JSX.Element => {
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
+        if (["FromDefinition", "KameletDefinition"].includes(element.dslName)) {
+            return k ? this.getIconFromSource(k.icon()) : CamelUi.getIconForDslName(element.dslName);
+        } else if (element.dslName === "ToDefinition" && (element as ToDefinition).uri?.startsWith("kamelet:")) {
+            return k ? this.getIconFromSource(k.icon()) : CamelUi.getIconForDslName(element.dslName);
+        } else {
+            return this.getIconForDslName(element.dslName);
+        }
+    }
+    static getIconForDslName = (dslName: string):JSX.Element => {
+        switch (dslName) {
+            case 'AggregateDefinition': return <AggregateIcon/>;
+            case 'ChoiceDefinition' :return <ChoiceIcon/>;
+            case 'SplitDefinition' :return <SplitIcon/>;
+            case 'SagaDefinition' :return <SagaIcon/>;
+            case 'TransformDefinition' :return <TransformIcon/>;
+            case 'FilterDefinition' :return <FilterIcon/>;
+            case 'SortDefinition' :return <SortIcon/>;
+            default: return this.getIconFromSource(CamelUi.getIconSrcForName(dslName))
+        }
+    }
+
+    static getIconFromSource = (src: string):JSX.Element => {
+        return <img draggable={false} src={src} className="icon" alt="icon"/>
+    }
+
+    static getConnectionIcon = (element: CamelElement): string => {
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
+        if (["FromDefinition", "KameletDefinition"].includes(element.dslName)) {
+            return k ? k.icon() : externalIcon;
+        } else if (element.dslName === "ToDefinition" && (element as ToDefinition).uri?.startsWith("kamelet:")) {
+            return k ? k.icon() : CamelUi.getIconSrcForName(element.dslName);
+        } else {
+            return externalIcon;
+        }
+    }
+
+    static getFlowCounts = (i: Integration): Map<string, number> => {
+        const result = new Map<string, number>();
+        result.set('routes', i.spec.flows?.filter((e: any) => e.dslName === 'RouteDefinition').length || 0);
+        result.set('rest', i.spec.flows?.filter((e: any) => e.dslName === 'RestDefinition').length || 0);
+        const beans = i.spec.flows?.filter((e: any) => e.dslName === 'Beans');
+        if (beans && beans.length > 0 && beans[0].beans && beans[0].beans.length > 0){
+            result.set('beans', Array.from(beans[0].beans).length);
+        }
+        return result;
+    }
+
+    static getRoutes = (integration: Integration): CamelElement[] => {
+        const result: CamelElement[] = [];
+        integration.spec.flows?.filter((e: any) => e.dslName === 'RouteDefinition')
+            .forEach((f: any) => result.push(f));
+        return result;
+    }
+
+    static getBeans = (integration: Integration): NamedBeanDefinition[] => {
+        const result: NamedBeanDefinition[] = [];
+        const beans = integration.spec.flows?.filter((e: any) => e.dslName === 'Beans');
+        if (beans && beans.length > 0 && beans[0].beans) {
+            result.push(...beans[0].beans);
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/utils/DslMetaModel.ts b/karavan-app/src/main/webui/src/designer/utils/DslMetaModel.ts
new file mode 100644
index 0000000..53318bd
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/utils/DslMetaModel.ts
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export class DslMetaModel {
+    dsl: string = ''
+    name: string = ''
+    type: string = ''
+    uri?: string
+    title: string = ''
+    description: string = ''
+    labels: string = ''
+    navigation: string = ''
+    version: string = ''
+    supportLevel: string = ''
+    properties: any;
+
+    public constructor(init?: Partial<DslMetaModel>) {
+        Object.assign(this, init);
+    }
+}
+
+export class DslProperty {
+    name: string = ''
+    type: string = ''
+    title: string = ''
+    description: string = ''
+    required: boolean = false;
+    secret: boolean = false
+    enum: any[] = []
+
+    public constructor(init?: Partial<DslProperty>) {
+        Object.assign(this, init);
+    }
+}
+
+export class DslConstraints {
+    constraints: DslConstraint[] = []
+
+    public constructor(init?: Partial<DslConstraints>) {
+        Object.assign(this, init);
+    }
+}
+
+export class DslConstraint {
+    name: string = ''
+    steps: string[] = []
+
+    public constructor(init?: Partial<DslConstraint>) {
+        Object.assign(this, init);
+    }
+}
+
+export class DslLanguage {
+    name: string = ''
+    title: string = ''
+    description: string = ''
+
+    public constructor(init?: Partial<DslLanguage>) {
+        Object.assign(this, init);
+    }
+
+    public toString() {
+        return this.title;
+    }
+
+    static getName(lang: DslLanguage) {
+        return lang.name;
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/utils/EventBus.ts b/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
new file mode 100644
index 0000000..98e573d
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Subject} from 'rxjs';
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+
+const positions = new Subject<DslPosition>();
+
+export class DslPosition {
+    step: CamelElement = new CamelElement("");
+    parent: CamelElement | undefined;
+    inSteps: boolean = false;
+    isSelected: boolean = false;
+    position: number = 0;
+    rect: DOMRect = new DOMRect();
+    headerRect: DOMRect = new DOMRect();
+    command: "add" | "delete" = "add";
+
+    constructor(command: "add" | "delete",
+                step: CamelElement,
+                parent:CamelElement | undefined,
+                rect: DOMRect,
+                headerRect:DOMRect,
+                position: number,
+                inSteps: boolean = false,
+                isSelected: boolean = false) {
+        this.command = command;
+        this.step = step;
+        this.parent = parent;
+        this.rect = rect;
+        this.headerRect = headerRect;
+        this.inSteps = inSteps;
+        this.position = position;
+        this.isSelected = isSelected;
+    }
+}
+
+export const EventBus = {
+    sendPosition: (command: "add" | "delete",
+                   step: CamelElement,
+                   parent: CamelElement | undefined,
+                   rect: DOMRect,
+                   headerRect: DOMRect,
+                   position: number,
+                   inSteps: boolean = false,
+                   isSelected: boolean = false) => positions.next(new DslPosition(command, step, parent, rect, headerRect, position, inSteps, isSelected)),
+    onPosition: () => positions.asObservable(),
+}
diff --git a/karavan-app/src/main/webui/src/designer/utils/KaravanComponents.tsx b/karavan-app/src/main/webui/src/designer/utils/KaravanComponents.tsx
new file mode 100644
index 0000000..be32ff7
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/utils/KaravanComponents.tsx
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {FormGroup, TextInput, Title} from "@patternfly/react-core";
+import {CamelUi} from "./CamelUi";
+import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+
+interface Props {
+    integration: Integration,
+}
+
+export class IntegrationHeader extends React.Component<Props> {
+
+    render() {
+        return (
+            <div className="headers">
+                <Title headingLevel="h1" size="md">Integration</Title>
+                <FormGroup label="Title" fieldId="title" isRequired>
+                    <TextInput className="text-field" type="text" id="title" name="title" isReadOnly
+                               value={
+                                   CamelUi.titleFromName(this.props.integration.metadata.name)
+                               }/>
+                </FormGroup>
+                <FormGroup label="Name" fieldId="name" isRequired>
+                    <TextInput className="text-field" type="text" id="name" name="name" isReadOnly
+                               value={this.props.integration.metadata.name}/>
+                </FormGroup>
+            </div>
+        )
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx b/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
new file mode 100644
index 0000000..a215143
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+
+export function getDesignerIcon(icon: string) {
+    if (icon === 'routes') return (
+        <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
+            <defs>
+                <style>{".cls-1{fill:none;}"}</style>
+            </defs>
+            <path d="M29,10H24v2h5v6H22v2h3v2.142a4,4,0,1,0,2,0V20h2a2.0027,2.0027,0,0,0,2-2V12A2.0023,2.0023,0,0,0,29,10ZM28,26a2,2,0,1,1-2-2A2.0027,2.0027,0,0,1,28,26Z"/>
+            <path d="M19,6H14V8h5v6H12v2h3v6.142a4,4,0,1,0,2,0V16h2a2.0023,2.0023,0,0,0,2-2V8A2.0023,2.0023,0,0,0,19,6ZM18,26a2,2,0,1,1-2-2A2.0027,2.0027,0,0,1,18,26Z"/>
+            <path
+                d="M9,2H3A2.002,2.002,0,0,0,1,4v6a2.002,2.002,0,0,0,2,2H5V22.142a4,4,0,1,0,2,0V12H9a2.002,2.002,0,0,0,2-2V4A2.002,2.002,0,0,0,9,2ZM8,26a2,2,0,1,1-2-2A2.0023,2.0023,0,0,1,8,26ZM3,10V4H9l.0015,6Z"/>
+            <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32" height="32"/>
+        </svg>)
+    if (icon === 'rest') return (
+        <svg className="top-icon" viewBox="0 0 32 32">
+            <g className="layer">
+                <title>Layer 1</title>
+                <path
+                    d="m23.50007,22l-0.5,0l0,-2l0.5,0a4.4975,4.4975 0 0 0 0.3564,-8.981l-0.8154,-0.0639l-0.0986,-0.812a6.9938,6.9938 0 0 0 -13.8838,0l-0.0991,0.812l-0.8155,0.0639a4.4975,4.4975 0 0 0 0.356,8.981l0.5,0l0,2l-0.5,0a6.4973,6.4973 0 0 1 -1.3,-12.8638a8.9943,8.9943 0 0 1 17.6006,0a6.4974,6.4974 0 0 1 -1.3006,12.8638z"
+                    id="svg_1"/>
+                <path
+                    d="m22.9724,22.26637l0,-2l-2.1011,0a4.9678,4.9678 0 0 0 -0.7319,-1.7529l1.49,-1.49l-1.414,-1.414l-1.49,1.49a4.9678,4.9678 0 0 0 -1.753,-0.732l0,-2.1011l-2,0l0,2.1011a4.9678,4.9678 0 0 0 -1.7529,0.7319l-1.49,-1.49l-1.414,1.414l1.49,1.49a4.9678,4.9678 0 0 0 -0.732,1.753l-2.1011,0l0,2l2.1011,0a4.9678,4.9678 0 0 0 0.7319,1.7529l-1.49,1.49l1.414,1.414l1.49,-1.49a4.9678,4.9678 0 0 0 1.753,0.732l0,2.1011l2,0l0,-2.1011a4.9678,4.9678 0 0 0 1.7529,-0.7319l1.49,1.49l1.414,-1.414 [...]
+                    id="svg_2" transform="rotate(25 15.9724 21.2664)" xmlns="http://www.w3.org/2000/svg"/>
+            </g>
+        </svg>
+    )
+    if (icon === 'beans') return (
+        <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
+            <defs>
+                <style>{".cls-1 {fill: none;}"}</style>
+            </defs>
+            <title>data--1</title>
+            <rect x="15" y="6" width="13" height="2"/>
+            <rect x="15" y="24" width="13" height="2"/>
+            <rect x="4" y="15" width="13" height="2"/>
+            <path d="M7,11a4,4,0,1,1,4-4A4,4,0,0,1,7,11ZM7,5A2,2,0,1,0,9,7,2,2,0,0,0,7,5Z" transform="translate(0 0)"/>
+            <path d="M7,29a4,4,0,1,1,4-4A4,4,0,0,1,7,29Zm0-6a2,2,0,1,0,2,2A2,2,0,0,0,7,23Z" transform="translate(0 0)"/>
+            <path d="M25,20a4,4,0,1,1,4-4A4,4,0,0,1,25,20Zm0-6a2,2,0,1,0,2,2A2,2,0,0,0,25,14Z" transform="translate(0 0)"/>
+            <g id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;">
+                <rect className="cls-1" width="32" height="32"/>
+            </g>
+        </svg>
+    )
+    if (icon === 'dependencies') return (
+        <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
+            <defs>
+                <style>{".cls-1 {fill: none;}"}</style>
+            </defs>
+            <title>application</title>
+            <path d="M16,18H6a2,2,0,0,1-2-2V6A2,2,0,0,1,6,4H16a2,2,0,0,1,2,2V16A2,2,0,0,1,16,18ZM6,6V16H16V6Z" transform="translate(0 0)"/>
+            <path d="M26,12v4H22V12h4m0-2H22a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2h4a2,2,0,0,0,2-2V12a2,2,0,0,0-2-2Z" transform="translate(0 0)"/>
+            <path d="M26,22v4H22V22h4m0-2H22a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2h4a2,2,0,0,0,2-2V22a2,2,0,0,0-2-2Z" transform="translate(0 0)"/>
+            <path d="M16,22v4H12V22h4m0-2H12a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2h4a2,2,0,0,0,2-2V22a2,2,0,0,0-2-2Z" transform="translate(0 0)"/>
+            <g id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;">
+                <rect className="cls-1" width="32" height="32"/>
+            </g>
+        </svg>
+    )
+    if (icon === 'error') return (
+        <svg className="top-icon" width="36px" height="36px" viewBox="0 0 36 36" version="1.1" preserveAspectRatio="xMidYMid meet">
+            <circle className="clr-i-outline clr-i-outline-path-1" cx="18" cy="26.06" r="1.33"/>
+            <path className="clr-i-outline clr-i-outline-path-2" d="M18,22.61a1,1,0,0,1-1-1v-12a1,1,0,1,1,2,0v12A1,1,0,0,1,18,22.61Z"/>
+            <path className="clr-i-outline clr-i-outline-path-3" d="M18,34A16,16,0,1,1,34,18,16,16,0,0,1,18,34ZM18,4A14,14,0,1,0,32,18,14,14,0,0,0,18,4Z"/>
+            <rect x="0" y="0" width="36" height="36" fillOpacity="0"/>
+        </svg>)
+    if (icon === 'exception') return (
+        <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
+            <defs>
+                <style>{".cls-1{fill:none;}"}</style>
+            </defs>
+            <title>misuse--alt</title>
+            <polygon points="21.41 23 16 17.591 10.59 23 9 21.41 14.409 16 9 10.591 10.591 9 16 14.409 21.409 9 23 10.591 17.591 16 23 21.41 21.41 23"/>
+            <path d="M16,4A12,12,0,1,1,4,16,12.0136,12.0136,0,0,1,16,4m0-2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Z" transform="translate(0)"/>
+            <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32" height="32"/>
+        </svg>)
+    if (icon === 'template') return (
+        <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon" xmlns="http://www.w3.org/2000/svg">
+            <defs>
+                <style>{".cls-1{fill:none;}"}</style>
+            </defs>
+            <title>code</title>
+            <polygon points="31 16 24 23 22.59 21.59 28.17 16 22.59 10.41 24 9 31 16"/>
+            <polygon points="1 16 8 9 9.41 10.41 3.83 16 9.41 21.59 8 23 1 16"/>
+            <rect x="5.91" y="15" width="20.17" height="2" transform="translate(-3.6 27.31) rotate(-75)"/>
+            <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32" height="32" transform="translate(0 32) rotate(-90)"/>
+        </svg>)
+    if (icon === 'yaml') return (
+        <svg className="top-icon" x="0px" y="0px" width="32px" height="32px"
+             viewBox="0 0 32 32">
+            <style type="text/css">{".st0{fill:none;}"}</style>
+            <title>document</title>
+            <path
+                d="M25.7,9.3l-7-7C18.5,2.1,18.3,2,18,2H8C6.9,2,6,2.9,6,4v24c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V10C26,9.7,25.9,9.5,25.7,9.3  z M18,4.4l5.6,5.6H18V4.4z M24,28H8V4h8v6c0,1.1,0.9,2,2,2h6V28z"/>
+            <rect x="10" y="22" width="12" height="2"/>
+            <rect x="10" y="16" width="12" height="2"/>
+            <rect className="st0" width="32" height="32"/>
+        </svg>)
+    if (icon === 'code') return (
+        <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
+            <defs>
+                <style>{".cls-1{fill:none;}"}</style>
+            </defs>
+            <title>code</title>
+            <polygon points="31 16 24 23 22.59 21.59 28.17 16 22.59 10.41 24 9 31 16"/>
+            <polygon points="1 16 8 9 9.41 10.41 3.83 16 9.41 21.59 8 23 1 16"/>
+            <rect x="5.91" y="15" width="20.17" height="2" transform="translate(-3.6 27.31) rotate(-75)"/>
+            <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32" height="32" transform="translate(0 32) rotate(-90)"/>
+        </svg>)
+}
+
+
+export class BeanIcon extends React.Component<any> {
+
+    render() {
+        return (
+            <svg className="icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
+                <defs>
+                    <style>{".cls-1 {fill: none;}"}</style>
+                </defs>
+                <title>data--1</title>
+                <rect x="15" y="6" width="13" height="2"/>
+                <rect x="15" y="24" width="13" height="2"/>
+                <rect x="4" y="15" width="13" height="2"/>
+                <path d="M7,11a4,4,0,1,1,4-4A4,4,0,0,1,7,11ZM7,5A2,2,0,1,0,9,7,2,2,0,0,0,7,5Z" transform="translate(0 0)"/>
+                <path d="M7,29a4,4,0,1,1,4-4A4,4,0,0,1,7,29Zm0-6a2,2,0,1,0,2,2A2,2,0,0,0,7,23Z" transform="translate(0 0)"/>
+                <path d="M25,20a4,4,0,1,1,4-4A4,4,0,0,1,25,20Zm0-6a2,2,0,1,0,2,2A2,2,0,0,0,25,14Z" transform="translate(0 0)"/>
+                <g id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;">
+                    <rect className="cls-1" width="32" height="32"/>
+                </g>
+            </svg>
+        )
+    }
+}
+
+export class DependencyIcon extends React.Component<any> {
+
+    render() {
+        return (
+            <svg className="icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
+                <defs>
+                    <style>{".cls-1 {fill: none;}"}</style>
+                </defs>
+                <title>application</title>
+                <path d="M16,18H6a2,2,0,0,1-2-2V6A2,2,0,0,1,6,4H16a2,2,0,0,1,2,2V16A2,2,0,0,1,16,18ZM6,6V16H16V6Z" transform="translate(0 0)"/>
+                <path d="M26,12v4H22V12h4m0-2H22a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2h4a2,2,0,0,0,2-2V12a2,2,0,0,0-2-2Z" transform="translate(0 0)"/>
+                <path d="M26,22v4H22V22h4m0-2H22a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2h4a2,2,0,0,0,2-2V22a2,2,0,0,0-2-2Z" transform="translate(0 0)"/>
+                <path d="M16,22v4H12V22h4m0-2H12a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2h4a2,2,0,0,0,2-2V22a2,2,0,0,0-2-2Z" transform="translate(0 0)"/>
+                <g id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;">
+                    <rect className="cls-1" width="32" height="32"/>
+                </g>
+            </svg>
+        )
+    }
+}
+
+export class RestIcon extends React.Component<any> {
+
+    render() {
+        return (
+            <svg className="icon" viewBox="0 0 32 32">
+                <g className="layer">
+                    <title>Layer 1</title>
+                    <path
+                        d="m23.50007,22l-0.5,0l0,-2l0.5,0a4.4975,4.4975 0 0 0 0.3564,-8.981l-0.8154,-0.0639l-0.0986,-0.812a6.9938,6.9938 0 0 0 -13.8838,0l-0.0991,0.812l-0.8155,0.0639a4.4975,4.4975 0 0 0 0.356,8.981l0.5,0l0,2l-0.5,0a6.4973,6.4973 0 0 1 -1.3,-12.8638a8.9943,8.9943 0 0 1 17.6006,0a6.4974,6.4974 0 0 1 -1.3006,12.8638z"
+                        id="svg_1"/>
+                    <path
+                        d="m22.9724,22.26637l0,-2l-2.1011,0a4.9678,4.9678 0 0 0 -0.7319,-1.7529l1.49,-1.49l-1.414,-1.414l-1.49,1.49a4.9678,4.9678 0 0 0 -1.753,-0.732l0,-2.1011l-2,0l0,2.1011a4.9678,4.9678 0 0 0 -1.7529,0.7319l-1.49,-1.49l-1.414,1.414l1.49,1.49a4.9678,4.9678 0 0 0 -0.732,1.753l-2.1011,0l0,2l2.1011,0a4.9678,4.9678 0 0 0 0.7319,1.7529l-1.49,1.49l1.414,1.414l1.49,-1.49a4.9678,4.9678 0 0 0 1.753,0.732l0,2.1011l2,0l0,-2.1011a4.9678,4.9678 0 0 0 1.7529,-0.7319l1.49,1.49l1.414,-1 [...]
+                        id="svg_2" transform="rotate(25 15.9724 21.2664)" xmlns="http://www.w3.org/2000/svg"/>
+                </g>
+            </svg>
+        )
+    }
+}
+
+export class ConceptIcon extends React.Component<any> {
+
+    render() {
+        return (
+            <svg className="icon" width="32px" height="32px" viewBox="0 0 32 32">
+                <defs>
+                    <style>{".cls-1 {fill: none;}"}</style>
+                </defs>
+                <title>concept</title>
+                <path
+                    d="M20.8851,19.4711a5.9609,5.9609,0,0,0,0-6.9422L23,10.4141l1.293,1.2929a.9995.9995,0,0,0,1.414,0l4-4a.9994.9994,0,0,0,0-1.414l-4-4a.9994.9994,0,0,0-1.414,0l-4,4a.9994.9994,0,0,0,0,1.414L21.5859,9l-2.1148,2.1149a5.9609,5.9609,0,0,0-6.9422,0L10,8.5859V2H2v8H8.5859l2.529,2.5289a5.9609,5.9609,0,0,0,0,6.9422L9,21.5859,7.707,20.293a.9994.9994,0,0,0-1.414,0l-4,4a.9994.9994,0,0,0,0,1.414l4,4a.9995.9995,0,0,0,1.414,0l4-4a.9994.9994,0,0,0,0-1.414L10.4141,23l2.1148-2.1149a5.960 [...]
+                <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32" height="32"/>
+            </svg>
+        )
+    }
+}
+
+export function AggregateIcon() {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
+            <path d="M496.2 417.71l-130.22 101.1c-.19.14-.39.29-.59.42a28.39 28.39 0 01-30.77 0c-.21-.13-.4-.28-.59-.42L203.8 417.71h292.4z"></path>
+            <path d="M516.1 426.23v202.1c0 4.12-3.34 7.46-7.45 7.46H191.36c-4.11 0-7.46-3.34-7.46-7.46V426.22l138.52 107.53c.68.53 1.31.98 1.94 1.38 7.79 5.04 16.72 7.55 25.66 7.55s17.86-2.52 25.66-7.55c.62-.4 1.25-.85 1.94-1.38l138.5-107.52zM247.14 358.45l-12.91 30.22-.03.06v.03c-.11.21-.21.43-.32.64s-.23.42-.36.61c-.08.14-.17.27-.27.4-.08.11-.16.21-.24.31-.1.13-.21.25-.31.36-.08.09-.16.18-.24.25-.05.06-.1.11-.16.15l-.27.25c-.17.15-.33.29-.51.42-.15.13-.3.23-.47.33-.19.13-.39.25-.59.36s [...]
+            <path d="M277.46 154.15V264.2c0 5.52-4.48 10-10 10H82.69c-5.52 0-10-4.48-10-10V154.14l82.79 62.29c5.77 4.34 12.69 6.51 19.6 6.51s13.83-2.17 19.6-6.51l82.78-62.28z"></path>
+            <g>
+                <path d="M610.57 145.6l-76.07 57.24c-5.52 4.16-13.24 4.16-18.76 0l-76.08-57.24h170.91z"></path>
+                <path d="M627.5 154.15V264.2c0 5.52-4.48 10-10 10H432.73c-5.52 0-10-4.48-10-10V154.14l82.79 62.29c5.77 4.34 12.69 6.51 19.6 6.51s13.83-2.17 19.6-6.51l82.78-62.28z"></path>
+            </g>
+        </svg>
+    );
+}
+
+export function ChoiceIcon() {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
+            <path d="M407.33 113.97V609.2c0 2.75-1.9 5-4.22 5H291.55c-2.33 0-4.22-2.25-4.22-5V113.97c0-2.76 1.89-5 4.22-5h111.56c2.32 0 4.22 2.24 4.22 5zM27.1 437.55l60.87-57.64c.95-.9 2.32-1.41 3.76-1.41H258.2c2.76 0 5 1.87 5 4.17v111.65c0 2.3-2.24 4.17-5 4.17H91.54c-1.38 0-2.7-.48-3.65-1.32L27.2 443.15c-1.77-1.58-1.81-3.99-.1-5.61zM667.57 285.62l-60.87 57.64c-.95.9-2.32 1.41-3.76 1.41H436.47c-2.76 0-5-1.87-5-4.17V228.85c0-2.3 2.24-4.17 5-4.17h166.66c1.38 0 2.7.48 3.65 1.32l60.69 54.02c [...]
+        </svg>
+    );
+}
+
+
+export function SplitIcon() {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
+            <path d="M496.2 83.65l-130.22 101.1c-.19.14-.39.29-.59.42a28.39 28.39 0 01-30.77 0c-.21-.13-.4-.28-.59-.42L203.8 83.65h292.4z"></path>
+            <path d="M516.1 92.17v202.1c0 4.12-3.34 7.46-7.45 7.46H191.36c-4.11 0-7.46-3.34-7.46-7.46V92.16l138.52 107.53c.68.53 1.31.98 1.94 1.38 7.79 5.04 16.72 7.55 25.66 7.55s17.86-2.52 25.66-7.55c.62-.4 1.25-.85 1.94-1.38l138.5-107.52zM524.34 397.22l-12.91 30.22-.03.06v.03c-.11.21-.21.43-.32.64s-.23.42-.36.61c-.08.14-.17.27-.27.4-.08.11-.16.21-.24.31-.1.13-.21.25-.31.36-.08.09-.16.18-.24.25-.05.06-.1.11-.16.15l-.27.25c-.17.15-.33.29-.51.42-.15.13-.3.23-.47.33-.19.13-.39.25-.59.36s-. [...]
+            <path d="M277.36 476.26v110.05c0 5.52-4.48 10-10 10H82.59c-5.52 0-10-4.48-10-10V476.25l82.79 62.29c5.77 4.34 12.69 6.51 19.6 6.51s13.83-2.17 19.6-6.51l82.78-62.28z"></path>
+            <g>
+                <path d="M610.48 467.71l-76.07 57.24c-5.52 4.16-13.24 4.16-18.76 0l-76.08-57.24h170.91z"></path>
+                <path d="M627.41 476.26v110.05c0 5.52-4.48 10-10 10H432.64c-5.52 0-10-4.48-10-10V476.25l82.79 62.29c5.77 4.34 12.69 6.51 19.6 6.51s13.83-2.17 19.6-6.51l82.78-62.28z"></path>
+            </g>
+        </svg>
+    );
+}
+
+export function SagaIcon() {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
+            <path d="M626.41 255.77c-.56-4.77-2.95-9.03-6.71-11.99l-46.46-36.64-1.06-1.09-.8-.28c-.81-.52-1.67-.98-2.56-1.36-.43-.19-.85-.36-1.25-.5-.47-.16-.96-.31-1.51-.45-.47-.11-.96-.22-1.45-.3-.49-.08-.97-.14-1.43-.18-.96-.08-1.95-.08-2.91-.01-.41.03-.83.08-1.23.14-.41.06-.82.14-1.25.23l-.58.14c-.1.03-.2.05-.31.08-.11.03-.21.06-.3.09-.29.08-.57.18-.86.28-.49.17-.99.37-1.53.61l-.16.08c-.32.15-.65.31-.97.49-.49.26-.93.53-1.34.81-.39.26-.76.52-1.12.8l-46.96 37.05a17.823 17.823 0 00-6.7 [...]
+        </svg>
+    );
+}
+
+export function TransformIcon() {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
+            <path d="M441.77 277.51l-82.73 64.23c-.07.05-.13.09-.19.13-5.37 3.48-12.33 3.48-17.69.01-.07-.05-.13-.09-.18-.13l-82.76-64.24h183.54z"></path>
+            <path d="M462.2 287.02V420.7c0 .98-.79 1.77-1.77 1.77H239.57c-.98 0-1.77-.79-1.77-1.77V287.02l90.91 70.56c.54.44 1.06.8 1.57 1.12 5.99 3.88 12.86 5.81 19.72 5.81s13.73-1.94 19.73-5.81c.49-.32 1.01-.68 1.58-1.13l90.89-70.55zM622.28 330.68l-35.89 31.78a1.48 1.48 0 01-1.98 0l-35.89-31.78c-.3-.26-.48-.63-.51-1.03-.02-.4.11-.79.38-1.09l11.28-12.73c.55-.61 1.49-.67 2.11-.12l12.44 11.02c-5.24-51.26-28.18-99.47-64.84-136.12-35.82-35.82-81.13-58.05-131.04-64.27-.1 0-.19-.03-.28-.06v.0 [...]
+        </svg>
+    );
+}
+
+export function FilterIcon() {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" id="a" viewBox="0 0 700 700" className="icon">
+            <path d="M565.62 156.56L413.36 350.33a10.032 10.032 0 00-2.14 6.18v190.52c0 19.05-25.01 34.49-55.86 34.49s-55.86-15.44-55.86-34.49V356.51c0-2.24-.75-4.42-2.14-6.18L145.1 156.56c-5.15-6.56-.48-16.18 7.87-16.18h404.79c8.34 0 13.02 9.62 7.86 16.18z"></path>
+        </svg>
+    );
+}
+
+export function SortIcon() {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
+            <defs>
+                <style>{".b{stroke-linejoin: round;}.b,.c {stroke-linecap: round;}.b,.c,.d {fill: none;stroke: #000;stroke-width: 35px;}.c,.d {stroke-miterlimit: 10;}"}</style>
+            </defs>
+            <path d="M160.63 168.63L160.63 531.37" className="d"></path>
+            <path d="M576.31 170.27L269.3 170.27" className="c"></path>
+            <path d="M517.53 290.64L269.3 290.64" className="c"></path>
+            <path d="M458.75 411L269.3 411" className="c"></path>
+            <path d="M399.97 531.37L269.3 531.37" className="c"></path>
+            <path d="M197.7 197.95L160.63 168.71 123.55 197.95" className="b"></path>
+            <path d="M123.55 502.12L160.62 531.37 197.7 502.12" className="b"></path>
+        </svg>
+    );
+}
+
+export function SpringIcon() {
+    return (
+        <svg
+            xmlns="http://www.w3.org/2000/svg"
+            width="32"
+            height="32"
+            viewBox="0 0 32 32"
+            className="icon">
+            <g fill="none" fillRule="evenodd">
+                <path d="M0 0h32v32H0z"></path>
+                <path
+                    fill="#70AD51"
+                    d="M5.466 27.993a1.364 1.364 0 10-.087-.076l-.266-.234C1.972 24.762 0 20.597 0 15.978 0 7.168 7.168 0 15.98 0c4.48 0 8.53 1.857 11.435 4.836a14.681 14.681 0 001.7-3.015c2.036 6.118 3.233 11.26 2.795 15.31-.592 8.274-7.508 14.83-15.93 14.83a15.903 15.903 0 01-10.276-3.757l-.238-.21zm23.58-4.982c4.01-5.336 1.775-13.965-.085-19.48-1.657 3.453-5.738 6.094-9.262 6.93-3.303.788-6.226.142-9.283 1.318-6.97 2.68-6.86 10.992-3.02 12.86.002 0 .23.124.227.12 0-.002 5.644-1.122 8. [...]
+                ></path>
+            </g>
+        </svg>
+    );
+}
+
+export function QuarkusIcon() {
+    return (
+        <svg
+            xmlns="http://www.w3.org/2000/svg"
+            width="257"
+            height="257"
+            preserveAspectRatio="xMidYMid"
+            viewBox="-0.5 0 257 257"
+            className="icon">
+            <path
+                fill="#4695EB"
+                d="M213.554 0c23.418.08 42.377 19.052 42.443 42.47v171.084c-.066 23.428-19.042 42.404-42.47 42.47h-25.439l-11.661-28.318h37.127c7.774-.1 14.051-6.378 14.152-14.152V42.47c-.1-7.774-6.378-14.051-14.152-14.152H42.47c-7.774.1-14.051 6.378-14.152 14.152v171.084c.1 7.774 6.378 14.051 14.152 14.152h62.607l22.935-48.494 31.625 76.812H42.47C19.042 255.958.066 236.982 0 213.554V42.47C.066 19.042 19.042.066 42.47 0h171.084zm-43.983 139.727v45.51l-39.417-22.748 39.417-22.762zM86.453  [...]
+            ></path>
+            <path
+                fill="#FF004A"
+                d="M86.453 139.727l39.417 22.762-39.417 22.748v-45.51zm83.118-45.509l39.418 22.748-39.418 22.761V94.218zM88.595 44.987l39.417 22.748-39.417 22.761V44.987z"
+            ></path>
+            <path
+                fill="#091313"
+                d="M86.453 94.218l39.417 22.748v45.523l-39.417-22.762V94.218zm83.118 0v45.51l-39.417 22.76v-45.522l39.417-22.748zm-41.559-26.483l39.417 22.761-39.417 22.761-39.417-22.76 39.417-22.762z"
+            ></path>
+        </svg>
+    );
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/utils/KubernetesAPI.ts b/karavan-app/src/main/webui/src/designer/utils/KubernetesAPI.ts
new file mode 100644
index 0000000..5168bc3
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/utils/KubernetesAPI.ts
@@ -0,0 +1,19 @@
+export class KubernetesAPI {
+
+    static inKubernetes: boolean = false;
+    static configMaps: string[] = [];
+    static secrets: string[] = [];
+    static services: string[] = [];
+
+    static setConfigMaps(configMaps: string[]){
+        this.configMaps = configMaps
+    }
+
+    static setSecrets(secrets: string[]){
+        this.secrets = secrets
+    }
+
+    static setServices(services: string[]){
+        this.services = services
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/utils/MediaTypes.ts b/karavan-app/src/main/webui/src/designer/utils/MediaTypes.ts
new file mode 100644
index 0000000..04ad09b
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/utils/MediaTypes.ts
@@ -0,0 +1,1485 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const MediaTypes: string[] = [
+    'application/1d-interleaved-parityfec',
+    'application/3gpdash-qoe-report+xml',
+    'application/3gppHal+json',
+    'application/3gppHalForms+json',
+    'application/3gpp-ims+xml',
+    'application/A2L',
+    'application/ace+cbor',
+    'application/activemessage',
+    'application/activity+json',
+    'application/alto-costmap+json',
+    'application/alto-costmapfilter+json',
+    'application/alto-directory+json',
+    'application/alto-endpointprop+json',
+    'application/alto-endpointpropparams+json',
+    'application/alto-endpointcost+json',
+    'application/alto-endpointcostparams+json',
+    'application/alto-error+json',
+    'application/alto-networkmapfilter+json',
+    'application/alto-networkmap+json',
+    'application/alto-updatestreamcontrol+json',
+    'application/alto-updatestreamparams+json',
+    'application/AML',
+    'application/andrew-inset',
+    'application/applefile',
+    'application/at+jwt',
+    'application/ATF',
+    'application/ATFX',
+    'application/atom+xml',
+    'application/atomcat+xml',
+    'application/atomdeleted+xml',
+    'application/atomicmail',
+    'application/atomsvc+xml',
+    'application/atsc-dwd+xml',
+    'application/atsc-dynamic-event-message',
+    'application/atsc-held+xml',
+    'application/atsc-rdt+json',
+    'application/atsc-rsat+xml',
+    'application/ATXML',
+    'application/auth-policy+xml',
+    'application/bacnet-xdd+zip',
+    'application/batch-SMTP',
+    'application/beep+xml',
+    'application/calendar+json',
+    'application/calendar+xml',
+    'application/call-completion',
+    'application/CALS-1840',
+    'application/captive+json',
+    'application/cbor',
+    'application/cbor-seq',
+    'application/cccex',
+    'application/ccmp+xml',
+    'application/ccxml+xml',
+    'application/CDFX+XML',
+    'application/cdmi-capability',
+    'application/cdmi-container',
+    'application/cdmi-domain',
+    'application/cdmi-object',
+    'application/cdmi-queue',
+    'application/cdni',
+    'application/CEA',
+    'application/cea-2018+xml',
+    'application/cellml+xml',
+    'application/cfw',
+    'application/city+json',
+    'application/clr',
+    'application/clue_info+xml',
+    'application/clue+xml',
+    'application/cms',
+    'application/cnrp+xml',
+    'application/coap-group+json',
+    'application/coap-payload',
+    'application/commonground',
+    'application/conference-info+xml',
+    'application/cpl+xml',
+    'application/cose',
+    'application/cose-key',
+    'application/cose-key-set',
+    'application/csrattrs',
+    'application/csta+xml',
+    'application/CSTAdata+xml',
+    'application/csvm+json',
+    'application/cwt',
+    'application/cybercash',
+    'application/dash+xml',
+    'application/dash-patch+xml',
+    'application/dashdelta',
+    'application/davmount+xml',
+    'application/dca-rft',
+    'application/DCD',
+    'application/dec-dx',
+    'application/dialog-info+xml',
+    'application/dicom',
+    'application/dicom+json',
+    'application/dicom+xml',
+    'application/DII',
+    'application/DIT',
+    'application/dns',
+    'application/dns+json',
+    'application/dns-message',
+    'application/dots+cbor',
+    'application/dskpp+xml',
+    'application/dssc+der',
+    'application/dssc+xml',
+    'application/dvcs',
+    'application/ecmascript',
+    'application/EDI-consent',
+    'application/EDIFACT',
+    'application/EDI-X12',
+    'application/efi',
+    'application/elm+json',
+    'application/elm+xml',
+    'application/EmergencyCallData.cap+xml',
+    'application/EmergencyCallData.Comment+xml',
+    'application/EmergencyCallData.Control+xml',
+    'application/EmergencyCallData.DeviceInfo+xml',
+    'application/EmergencyCallData.eCall.MSD',
+    'application/EmergencyCallData.ProviderInfo+xml',
+    'application/EmergencyCallData.ServiceInfo+xml',
+    'application/EmergencyCallData.SubscriberInfo+xml',
+    'application/EmergencyCallData.VEDS+xml',
+    'application/emma+xml',
+    'application/emotionml+xml',
+    'application/encaprtp',
+    'application/epp+xml',
+    'application/epub+zip',
+    'application/eshop',
+    'application/example',
+    'application/exi',
+    'application/expect-ct-report+json',
+    'application/express',
+    'application/fastinfoset',
+    'application/fastsoap',
+    'application/fdt+xml',
+    'application/fhir+json',
+    'application/fhir+xml',
+    'application/fits',
+    'application/flexfec',
+    'application/font-sfnt',
+    'application/font-tdpfr',
+    'application/font-woff',
+    'application/framework-attributes+xml',
+    'application/geo+json',
+    'application/geo+json-seq',
+    'application/geopackage+sqlite3',
+    'application/geoxacml+xml',
+    'application/gltf-buffer',
+    'application/gml+xml',
+    'application/gzip',
+    'application/H224',
+    'application/held+xml',
+    'application/http',
+    'application/hyperstudio',
+    'application/ibe-key-request+xml',
+    'application/ibe-pkg-reply+xml',
+    'application/ibe-pp-data',
+    'application/iges',
+    'application/im-iscomposing+xml',
+    'application/index',
+    'application/index.cmd',
+    'application/index.obj',
+    'application/index.response',
+    'application/index.vnd',
+    'application/inkml+xml',
+    'application/IOTP',
+    'application/ipfix',
+    'application/ipp',
+    'application/ISUP',
+    'application/its+xml',
+    'application/javascript',
+    'application/jf2feed+json',
+    'application/jose',
+    'application/jose+json',
+    'application/jrd+json',
+    'application/jscalendar+json',
+    'application/json',
+    'application/json-patch+json',
+    'application/json-seq',
+    'application/jwk+json',
+    'application/jwk-set+json',
+    'application/jwt',
+    'application/kpml-request+xml',
+    'application/kpml-response+xml',
+    'application/ld+json',
+    'application/lgr+xml',
+    'application/link-format',
+    'application/load-control+xml',
+    'application/lost+xml',
+    'application/lostsync+xml',
+    'application/lpf+zip',
+    'application/LXF',
+    'application/mac-binhex40',
+    'application/macwriteii',
+    'application/mads+xml',
+    'application/manifest+json',
+    'application/marc',
+    'application/marcxml+xml',
+    'application/mathematica',
+    'application/mathml+xml',
+    'application/mathml-content+xml',
+    'application/mathml-presentation+xml',
+    'application/mbms-associated-procedure-description+xml',
+    'application/mbms-deregister+xml',
+    'application/mbms-envelope+xml',
+    'application/mbms-msk-response+xml',
+    'application/mbms-msk+xml',
+    'application/mbms-protection-description+xml',
+    'application/mbms-reception-report+xml',
+    'application/mbms-register-response+xml',
+    'application/mbms-register+xml',
+    'application/mbms-schedule+xml',
+    'application/mbms-user-service-description+xml',
+    'application/mbox',
+    'application/media_control+xml',
+    'application/media-policy-dataset+xml',
+    'application/mediaservercontrol+xml',
+    'application/merge-patch+json',
+    'application/metalink4+xml',
+    'application/mets+xml',
+    'application/MF4',
+    'application/mikey',
+    'application/mipc',
+    'application/missing-blocks+cbor-seq',
+    'application/mmt-aei+xml',
+    'application/mmt-usd+xml',
+    'application/mods+xml',
+    'application/moss-keys',
+    'application/moss-signature',
+    'application/mosskey-data',
+    'application/mosskey-request',
+    'application/mp21',
+    'application/mp4',
+    'application/mpeg4-generic',
+    'application/mpeg4-iod',
+    'application/mpeg4-iod-xmt',
+    'application/mrb-consumer+xml',
+    'application/mrb-publish+xml',
+    'application/msc-ivr+xml',
+    'application/msc-mixer+xml',
+    'application/msword',
+    'application/mud+json',
+    'application/multipart-core',
+    'application/mxf',
+    'application/n-quads',
+    'application/n-triples',
+    'application/nasdata',
+    'application/news-checkgroups',
+    'application/news-groupinfo',
+    'application/news-transmission',
+    'application/nlsml+xml',
+    'application/node',
+    'application/nss',
+    'application/oauth-authz-req+jwt',
+    'application/oblivious-dns-message',
+    'application/ocsp-request',
+    'application/ocsp-response',
+    'application/octet-stream',
+    'application/ODA',
+    'application/odm+xml',
+    'application/ODX',
+    'application/oebps-package+xml',
+    'application/ogg',
+    'application/opc-nodeset+xml',
+    'application/oscore',
+    'application/oxps',
+    'application/p21',
+    'application/p21+zip',
+    'application/p2p-overlay+xml',
+    'application/parityfec',
+    'application/passport',
+    'application/patch-ops-error+xml',
+    'application/pdf',
+    'application/PDX',
+    'application/pem-certificate-chain',
+    'application/pgp-encrypted',
+    'application/pgp-keys',
+    'application/pgp-signature',
+    'application/pidf-diff+xml',
+    'application/pidf+xml',
+    'application/pkcs10',
+    'application/pkcs7-mime',
+    'application/pkcs7-signature',
+    'application/pkcs8',
+    'application/pkcs8-encrypted',
+    'application/pkcs12',
+    'application/pkix-attr-cert',
+    'application/pkix-cert',
+    'application/pkix-crl',
+    'application/pkix-pkipath',
+    'application/pkixcmp',
+    'application/pls+xml',
+    'application/poc-settings+xml',
+    'application/postscript',
+    'application/ppsp-tracker+json',
+    'application/problem+json',
+    'application/problem+xml',
+    'application/provenance+xml',
+    'application/prs.alvestrand.titrax-sheet',
+    'application/prs.cww',
+    'application/prs.cyn',
+    'application/prs.hpub+zip',
+    'application/prs.nprend',
+    'application/prs.plucker',
+    'application/prs.rdf-xml-crypt',
+    'application/prs.xsf+xml',
+    'application/pskc+xml',
+    'application/pvd+json',
+    'application/rdf+xml',
+    'application/route-apd+xml',
+    'application/route-s-tsid+xml',
+    'application/route-usd+xml',
+    'application/QSIG',
+    'application/raptorfec',
+    'application/rdap+json',
+    'application/reginfo+xml',
+    'application/relax-ng-compact-syntax',
+    'application/remote-printing',
+    'application/reputon+json',
+    'application/resource-lists-diff+xml',
+    'application/resource-lists+xml',
+    'application/rfc+xml',
+    'application/riscos',
+    'application/rlmi+xml',
+    'application/rls-services+xml',
+    'application/rpki-ghostbusters',
+    'application/rpki-manifest',
+    'application/rpki-publication',
+    'application/rpki-roa',
+    'application/rpki-updown',
+    'application/rtf',
+    'application/rtploopback',
+    'application/rtx',
+    'application/samlassertion+xml',
+    'application/samlmetadata+xml',
+    'application/sarif-external-properties+json',
+    'application/sarif+json',
+    'application/sbe',
+    'application/sbml+xml',
+    'application/scaip+xml',
+    'application/scim+json',
+    'application/scvp-cv-request',
+    'application/scvp-cv-response',
+    'application/scvp-vp-request',
+    'application/scvp-vp-response',
+    'application/sdp',
+    'application/secevent+jwt',
+    'application/senml-etch+cbor',
+    'application/senml-etch+json',
+    'application/senml-exi',
+    'application/senml+cbor',
+    'application/senml+json',
+    'application/senml+xml',
+    'application/sensml-exi',
+    'application/sensml+cbor',
+    'application/sensml+json',
+    'application/sensml+xml',
+    'application/sep-exi',
+    'application/sep+xml',
+    'application/session-info',
+    'application/set-payment',
+    'application/set-payment-initiation',
+    'application/set-registration',
+    'application/set-registration-initiation',
+    'application/SGML',
+    'application/sgml-open-catalog',
+    'application/shf+xml',
+    'application/sieve',
+    'application/simple-filter+xml',
+    'application/simple-message-summary',
+    'application/simpleSymbolContainer',
+    'application/sipc',
+    'application/slate',
+    'application/smil',
+    'application/smil+xml',
+    'application/smpte336m',
+    'application/soap+fastinfoset',
+    'application/soap+xml',
+    'application/sparql-query',
+    'application/spdx+json',
+    'application/sparql-results+xml',
+    'application/spirits-event+xml',
+    'application/sql',
+    'application/srgs',
+    'application/srgs+xml',
+    'application/sru+xml',
+    'application/ssml+xml',
+    'application/stix+json',
+    'application/swid+xml',
+    'application/tamp-apex-update',
+    'application/tamp-apex-update-confirm',
+    'application/tamp-community-update',
+    'application/tamp-community-update-confirm',
+    'application/tamp-error',
+    'application/tamp-sequence-adjust',
+    'application/tamp-sequence-adjust-confirm',
+    'application/tamp-status-query',
+    'application/tamp-status-response',
+    'application/tamp-update',
+    'application/tamp-update-confirm',
+    'application/taxii+json',
+    'application/td+json',
+    'application/tei+xml',
+    'application/TETRA_ISI',
+    'application/thraud+xml',
+    'application/timestamp-query',
+    'application/timestamp-reply',
+    'application/timestamped-data',
+    'application/tlsrpt+gzip',
+    'application/tlsrpt+json',
+    'application/tnauthlist',
+    'application/token-introspection+jwt',
+    'application/trickle-ice-sdpfrag',
+    'application/trig',
+    'application/ttml+xml',
+    'application/tve-trigger',
+    'application/tzif',
+    'application/tzif-leap',
+    'application/ulpfec',
+    'application/urc-grpsheet+xml',
+    'application/urc-ressheet+xml',
+    'application/urc-targetdesc+xml',
+    'application/urc-uisocketdesc+xml',
+    'application/vcard+json',
+    'application/vcard+xml',
+    'application/vemmi',
+    'application/vnd.1000minds.decision-model+xml',
+    'application/vnd.3gpp.5gnas',
+    'application/vnd.3gpp.access-transfer-events+xml',
+    'application/vnd.3gpp.bsf+xml',
+    'application/vnd.3gpp.GMOP+xml',
+    'application/vnd.3gpp.gtpc',
+    'application/vnd.3gpp.interworking-data',
+    'application/vnd.3gpp.lpp',
+    'application/vnd.3gpp.mc-signalling-ear',
+    'application/vnd.3gpp.mcdata-affiliation-command+xml',
+    'application/vnd.3gpp.mcdata-info+xml',
+    'application/vnd.3gpp.mcdata-payload',
+    'application/vnd.3gpp.mcdata-service-config+xml',
+    'application/vnd.3gpp.mcdata-signalling',
+    'application/vnd.3gpp.mcdata-ue-config+xml',
+    'application/vnd.3gpp.mcdata-user-profile+xml',
+    'application/vnd.3gpp.mcptt-affiliation-command+xml',
+    'application/vnd.3gpp.mcptt-floor-request+xml',
+    'application/vnd.3gpp.mcptt-info+xml',
+    'application/vnd.3gpp.mcptt-location-info+xml',
+    'application/vnd.3gpp.mcptt-mbms-usage-info+xml',
+    'application/vnd.3gpp.mcptt-service-config+xml',
+    'application/vnd.3gpp.mcptt-signed+xml',
+    'application/vnd.3gpp.mcptt-ue-config+xml',
+    'application/vnd.3gpp.mcptt-ue-init-config+xml',
+    'application/vnd.3gpp.mcptt-user-profile+xml',
+    'application/vnd.3gpp.mcvideo-affiliation-command+xml',
+    'application/vnd.3gpp.mcvideo-affiliation-info+xml',
+    'application/vnd.3gpp.mcvideo-info+xml',
+    'application/vnd.3gpp.mcvideo-location-info+xml',
+    'application/vnd.3gpp.mcvideo-mbms-usage-info+xml',
+    'application/vnd.3gpp.mcvideo-service-config+xml',
+    'application/vnd.3gpp.mcvideo-transmission-request+xml',
+    'application/vnd.3gpp.mcvideo-ue-config+xml',
+    'application/vnd.3gpp.mcvideo-user-profile+xml',
+    'application/vnd.3gpp.mid-call+xml',
+    'application/vnd.3gpp.ngap',
+    'application/vnd.3gpp.pfcp',
+    'application/vnd.3gpp.pic-bw-large',
+    'application/vnd.3gpp.pic-bw-small',
+    'application/vnd.3gpp.pic-bw-var',
+    'application/vnd.3gpp-prose-pc3ch+xml',
+    'application/vnd.3gpp-prose+xml',
+    'application/vnd.3gpp.s1ap',
+    'application/vnd.3gpp.sms',
+    'application/vnd.3gpp.sms+xml',
+    'application/vnd.3gpp.srvcc-ext+xml',
+    'application/vnd.3gpp.SRVCC-info+xml',
+    'application/vnd.3gpp.state-and-event-info+xml',
+    'application/vnd.3gpp.ussd+xml',
+    'application/vnd.3gpp-v2x-local-service-information',
+    'application/vnd.3gpp2.bcmcsinfo+xml',
+    'application/vnd.3gpp2.sms',
+    'application/vnd.3gpp2.tcap',
+    'application/vnd.3lightssoftware.imagescal',
+    'application/vnd.3M.Post-it-Notes',
+    'application/vnd.accpac.simply.aso',
+    'application/vnd.accpac.simply.imp',
+    'application/vnd.acucobol',
+    'application/vnd.acucorp',
+    'application/vnd.adobe.flash.movie',
+    'application/vnd.adobe.formscentral.fcdt',
+    'application/vnd.adobe.fxp',
+    'application/vnd.adobe.partial-upload',
+    'application/vnd.adobe.xdp+xml',
+    'application/vnd.adobe.xfdf',
+    'application/vnd.aether.imp',
+    'application/vnd.afpc.afplinedata',
+    'application/vnd.afpc.afplinedata-pagedef',
+    'application/vnd.afpc.cmoca-cmresource',
+    'application/vnd.afpc.foca-charset',
+    'application/vnd.afpc.foca-codedfont',
+    'application/vnd.afpc.foca-codepage',
+    'application/vnd.afpc.modca',
+    'application/vnd.afpc.modca-cmtable',
+    'application/vnd.afpc.modca-formdef',
+    'application/vnd.afpc.modca-mediummap',
+    'application/vnd.afpc.modca-objectcontainer',
+    'application/vnd.afpc.modca-overlay',
+    'application/vnd.afpc.modca-pagesegment',
+    'application/vnd.age',
+    'application/vnd.ah-barcode',
+    'application/vnd.ahead.space',
+    'application/vnd.airzip.filesecure.azf',
+    'application/vnd.airzip.filesecure.azs',
+    'application/vnd.amadeus+json',
+    'application/vnd.amazon.mobi8-ebook',
+    'application/vnd.americandynamics.acc',
+    'application/vnd.amiga.ami',
+    'application/vnd.amundsen.maze+xml',
+    'application/vnd.android.ota',
+    'application/vnd.anki',
+    'application/vnd.anser-web-certificate-issue-initiation',
+    'application/vnd.antix.game-component',
+    'application/vnd.apache.arrow.file',
+    'application/vnd.apache.arrow.stream',
+    'application/vnd.apache.thrift.binary',
+    'application/vnd.apache.thrift.compact',
+    'application/vnd.apache.thrift.json',
+    'application/vnd.api+json',
+    'application/vnd.aplextor.warrp+json',
+    'application/vnd.apothekende.reservation+json',
+    'application/vnd.apple.installer+xml',
+    'application/vnd.apple.keynote',
+    'application/vnd.apple.mpegurl',
+    'application/vnd.apple.numbers',
+    'application/vnd.apple.pages',
+    'application/vnd.arastra.swi',
+    'application/vnd.aristanetworks.swi',
+    'application/vnd.artisan+json',
+    'application/vnd.artsquare',
+    'application/vnd.astraea-software.iota',
+    'application/vnd.audiograph',
+    'application/vnd.autopackage',
+    'application/vnd.avalon+json',
+    'application/vnd.avistar+xml',
+    'application/vnd.balsamiq.bmml+xml',
+    'application/vnd.banana-accounting',
+    'application/vnd.bbf.usp.error',
+    'application/vnd.bbf.usp.msg',
+    'application/vnd.bbf.usp.msg+json',
+    'application/vnd.balsamiq.bmpr',
+    'application/vnd.bekitzur-stech+json',
+    'application/vnd.bint.med-content',
+    'application/vnd.biopax.rdf+xml',
+    'application/vnd.blink-idb-value-wrapper',
+    'application/vnd.blueice.multipass',
+    'application/vnd.bluetooth.ep.oob',
+    'application/vnd.bluetooth.le.oob',
+    'application/vnd.bmi',
+    'application/vnd.bpf',
+    'application/vnd.bpf3',
+    'application/vnd.businessobjects',
+    'application/vnd.byu.uapi+json',
+    'application/vnd.cab-jscript',
+    'application/vnd.canon-cpdl',
+    'application/vnd.canon-lips',
+    'application/vnd.capasystems-pg+json',
+    'application/vnd.cendio.thinlinc.clientconf',
+    'application/vnd.century-systems.tcp_stream',
+    'application/vnd.chemdraw+xml',
+    'application/vnd.chess-pgn',
+    'application/vnd.chipnuts.karaoke-mmd',
+    'application/vnd.ciedi',
+    'application/vnd.cinderella',
+    'application/vnd.cirpack.isdn-ext',
+    'application/vnd.citationstyles.style+xml',
+    'application/vnd.claymore',
+    'application/vnd.cloanto.rp9',
+    'application/vnd.clonk.c4group',
+    'application/vnd.cluetrust.cartomobile-config',
+    'application/vnd.cluetrust.cartomobile-config-pkg',
+    'application/vnd.coffeescript',
+    'application/vnd.collabio.xodocuments.document',
+    'application/vnd.collabio.xodocuments.document-template',
+    'application/vnd.collabio.xodocuments.presentation',
+    'application/vnd.collabio.xodocuments.presentation-template',
+    'application/vnd.collabio.xodocuments.spreadsheet',
+    'application/vnd.collabio.xodocuments.spreadsheet-template',
+    'application/vnd.collection.doc+json',
+    'application/vnd.collection+json',
+    'application/vnd.collection.next+json',
+    'application/vnd.comicbook-rar',
+    'application/vnd.comicbook+zip',
+    'application/vnd.commerce-battelle',
+    'application/vnd.commonspace',
+    'application/vnd.coreos.ignition+json',
+    'application/vnd.cosmocaller',
+    'application/vnd.contact.cmsg',
+    'application/vnd.crick.clicker',
+    'application/vnd.crick.clicker.keyboard',
+    'application/vnd.crick.clicker.palette',
+    'application/vnd.crick.clicker.template',
+    'application/vnd.crick.clicker.wordbank',
+    'application/vnd.criticaltools.wbs+xml',
+    'application/vnd.cryptii.pipe+json',
+    'application/vnd.crypto-shade-file',
+    'application/vnd.cryptomator.encrypted',
+    'application/vnd.cryptomator.vault',
+    'application/vnd.ctc-posml',
+    'application/vnd.ctct.ws+xml',
+    'application/vnd.cups-pdf',
+    'application/vnd.cups-postscript',
+    'application/vnd.cups-ppd',
+    'application/vnd.cups-raster',
+    'application/vnd.cups-raw',
+    'application/vnd.curl',
+    'application/vnd.cyan.dean.root+xml',
+    'application/vnd.cybank',
+    'application/vnd.cyclonedx+json',
+    'application/vnd.cyclonedx+xml',
+    'application/vnd.d2l.coursepackage1p0+zip',
+    'application/vnd.d3m-dataset',
+    'application/vnd.d3m-problem',
+    'application/vnd.dart',
+    'application/vnd.data-vision.rdz',
+    'application/vnd.datapackage+json',
+    'application/vnd.dataresource+json',
+    'application/vnd.dbf',
+    'application/vnd.debian.binary-package',
+    'application/vnd.dece.data',
+    'application/vnd.dece.ttml+xml',
+    'application/vnd.dece.unspecified',
+    'application/vnd.dece.zip',
+    'application/vnd.denovo.fcselayout-link',
+    'application/vnd.desmume.movie',
+    'application/vnd.dir-bi.plate-dl-nosuffix',
+    'application/vnd.dm.delegation+xml',
+    'application/vnd.dna',
+    'application/vnd.document+json',
+    'application/vnd.dolby.mobile.1',
+    'application/vnd.dolby.mobile.2',
+    'application/vnd.doremir.scorecloud-binary-document',
+    'application/vnd.dpgraph',
+    'application/vnd.dreamfactory',
+    'application/vnd.drive+json',
+    'application/vnd.dtg.local',
+    'application/vnd.dtg.local.flash',
+    'application/vnd.dtg.local.html',
+    'application/vnd.dvb.ait',
+    'application/vnd.dvb.dvbisl+xml',
+    'application/vnd.dvb.dvbj',
+    'application/vnd.dvb.esgcontainer',
+    'application/vnd.dvb.ipdcdftnotifaccess',
+    'application/vnd.dvb.ipdcesgaccess',
+    'application/vnd.dvb.ipdcesgaccess2',
+    'application/vnd.dvb.ipdcesgpdd',
+    'application/vnd.dvb.ipdcroaming',
+    'application/vnd.dvb.iptv.alfec-base',
+    'application/vnd.dvb.iptv.alfec-enhancement',
+    'application/vnd.dvb.notif-aggregate-root+xml',
+    'application/vnd.dvb.notif-container+xml',
+    'application/vnd.dvb.notif-generic+xml',
+    'application/vnd.dvb.notif-ia-msglist+xml',
+    'application/vnd.dvb.notif-ia-registration-request+xml',
+    'application/vnd.dvb.notif-ia-registration-response+xml',
+    'application/vnd.dvb.notif-init+xml',
+    'application/vnd.dvb.pfr',
+    'application/vnd.dvb.service',
+    'application/vnd.dxr',
+    'application/vnd.dynageo',
+    'application/vnd.dzr',
+    'application/vnd.easykaraoke.cdgdownload',
+    'application/vnd.ecip.rlp',
+    'application/vnd.ecdis-update',
+    'application/vnd.eclipse.ditto+json',
+    'application/vnd.ecowin.chart',
+    'application/vnd.ecowin.filerequest',
+    'application/vnd.ecowin.fileupdate',
+    'application/vnd.ecowin.series',
+    'application/vnd.ecowin.seriesrequest',
+    'application/vnd.ecowin.seriesupdate',
+    'application/vnd.efi.img',
+    'application/vnd.efi.iso',
+    'application/vnd.emclient.accessrequest+xml',
+    'application/vnd.enliven',
+    'application/vnd.enphase.envoy',
+    'application/vnd.eprints.data+xml',
+    'application/vnd.epson.esf',
+    'application/vnd.epson.msf',
+    'application/vnd.epson.quickanime',
+    'application/vnd.epson.salt',
+    'application/vnd.epson.ssf',
+    'application/vnd.ericsson.quickcall',
+    'application/vnd.espass-espass+zip',
+    'application/vnd.eszigno3+xml',
+    'application/vnd.etsi.aoc+xml',
+    'application/vnd.etsi.asic-s+zip',
+    'application/vnd.etsi.asic-e+zip',
+    'application/vnd.etsi.cug+xml',
+    'application/vnd.etsi.iptvcommand+xml',
+    'application/vnd.etsi.iptvdiscovery+xml',
+    'application/vnd.etsi.iptvprofile+xml',
+    'application/vnd.etsi.iptvsad-bc+xml',
+    'application/vnd.etsi.iptvsad-cod+xml',
+    'application/vnd.etsi.iptvsad-npvr+xml',
+    'application/vnd.etsi.iptvservice+xml',
+    'application/vnd.etsi.iptvsync+xml',
+    'application/vnd.etsi.iptvueprofile+xml',
+    'application/vnd.etsi.mcid+xml',
+    'application/vnd.etsi.mheg5',
+    'application/vnd.etsi.overload-control-policy-dataset+xml',
+    'application/vnd.etsi.pstn+xml',
+    'application/vnd.etsi.sci+xml',
+    'application/vnd.etsi.simservs+xml',
+    'application/vnd.etsi.timestamp-token',
+    'application/vnd.etsi.tsl+xml',
+    'application/vnd.etsi.tsl.der',
+    'application/vnd.eu.kasparian.car+json',
+    'application/vnd.eudora.data',
+    'application/vnd.evolv.ecig.profile',
+    'application/vnd.evolv.ecig.settings',
+    'application/vnd.evolv.ecig.theme',
+    'application/vnd.exstream-empower+zip',
+    'application/vnd.exstream-package',
+    'application/vnd.ezpix-album',
+    'application/vnd.ezpix-package',
+    'application/vnd.f-secure.mobile',
+    'application/vnd.fastcopy-disk-image',
+    'application/vnd.familysearch.gedcom+zip',
+    'application/vnd.fdf',
+    'application/vnd.fdsn.mseed',
+    'application/vnd.fdsn.seed',
+    'application/vnd.ffsns',
+    'application/vnd.ficlab.flb+zip',
+    'application/vnd.filmit.zfc',
+    'application/vnd.fints',
+    'application/vnd.firemonkeys.cloudcell',
+    'application/vnd.FloGraphIt',
+    'application/vnd.fluxtime.clip',
+    'application/vnd.font-fontforge-sfd',
+    'application/vnd.framemaker',
+    'application/vnd.frogans.fnc',
+    'application/vnd.frogans.ltf',
+    'application/vnd.fsc.weblaunch',
+    'application/vnd.fujifilm.fb.docuworks',
+    'application/vnd.fujifilm.fb.docuworks.binder',
+    'application/vnd.fujifilm.fb.docuworks.container',
+    'application/vnd.fujifilm.fb.jfi+xml',
+    'application/vnd.fujitsu.oasys',
+    'application/vnd.fujitsu.oasys2',
+    'application/vnd.fujitsu.oasys3',
+    'application/vnd.fujitsu.oasysgp',
+    'application/vnd.fujitsu.oasysprs',
+    'application/vnd.fujixerox.ART4',
+    'application/vnd.fujixerox.ART-EX',
+    'application/vnd.fujixerox.ddd',
+    'application/vnd.fujixerox.docuworks',
+    'application/vnd.fujixerox.docuworks.binder',
+    'application/vnd.fujixerox.docuworks.container',
+    'application/vnd.fujixerox.HBPL',
+    'application/vnd.fut-misnet',
+    'application/vnd.futoin+cbor',
+    'application/vnd.futoin+json',
+    'application/vnd.fuzzysheet',
+    'application/vnd.genomatix.tuxedo',
+    'application/vnd.gentics.grd+json',
+    'application/vnd.geo+json',
+    'application/vnd.geocube+xml',
+    'application/vnd.geogebra.file',
+    'application/vnd.geogebra.slides',
+    'application/vnd.geogebra.tool',
+    'application/vnd.geometry-explorer',
+    'application/vnd.geonext',
+    'application/vnd.geoplan',
+    'application/vnd.geospace',
+    'application/vnd.gerber',
+    'application/vnd.globalplatform.card-content-mgt',
+    'application/vnd.globalplatform.card-content-mgt-response',
+    'application/vnd.gmx',
+    'application/vnd.google-earth.kml+xml',
+    'application/vnd.google-earth.kmz',
+    'application/vnd.gov.sk.e-form+xml',
+    'application/vnd.gov.sk.e-form+zip',
+    'application/vnd.gov.sk.xmldatacontainer+xml',
+    'application/vnd.grafeq',
+    'application/vnd.gridmp',
+    'application/vnd.groove-account',
+    'application/vnd.groove-help',
+    'application/vnd.groove-identity-message',
+    'application/vnd.groove-injector',
+    'application/vnd.groove-tool-message',
+    'application/vnd.groove-tool-template',
+    'application/vnd.groove-vcard',
+    'application/vnd.hal+json',
+    'application/vnd.hal+xml',
+    'application/vnd.HandHeld-Entertainment+xml',
+    'application/vnd.hbci',
+    'application/vnd.hc+json',
+    'application/vnd.hcl-bireports',
+    'application/vnd.hdt',
+    'application/vnd.heroku+json',
+    'application/vnd.hhe.lesson-player',
+    'application/vnd.hl7cda+xml',
+    'application/vnd.hl7v2+xml',
+    'application/vnd.hp-HPGL',
+    'application/vnd.hp-hpid',
+    'application/vnd.hp-hps',
+    'application/vnd.hp-jlyt',
+    'application/vnd.hp-PCL',
+    'application/vnd.hp-PCLXL',
+    'application/vnd.httphone',
+    'application/vnd.hydrostatix.sof-data',
+    'application/vnd.hyper-item+json',
+    'application/vnd.hyper+json',
+    'application/vnd.hyperdrive+json',
+    'application/vnd.hzn-3d-crossword',
+    'application/vnd.ibm.afplinedata',
+    'application/vnd.ibm.electronic-media',
+    'application/vnd.ibm.MiniPay',
+    'application/vnd.ibm.modcap',
+    'application/vnd.ibm.rights-management',
+    'application/vnd.ibm.secure-container',
+    'application/vnd.iccprofile',
+    'application/vnd.ieee.1905',
+    'application/vnd.igloader',
+    'application/vnd.imagemeter.folder+zip',
+    'application/vnd.imagemeter.image+zip',
+    'application/vnd.immervision-ivp',
+    'application/vnd.immervision-ivu',
+    'application/vnd.ims.imsccv1p1',
+    'application/vnd.ims.imsccv1p2',
+    'application/vnd.ims.imsccv1p3',
+    'application/vnd.ims.lis.v2.result+json',
+    'application/vnd.ims.lti.v2.toolconsumerprofile+json',
+    'application/vnd.ims.lti.v2.toolproxy.id+json',
+    'application/vnd.ims.lti.v2.toolproxy+json',
+    'application/vnd.ims.lti.v2.toolsettings+json',
+    'application/vnd.ims.lti.v2.toolsettings.simple+json',
+    'application/vnd.informedcontrol.rms+xml',
+    'application/vnd.infotech.project',
+    'application/vnd.infotech.project+xml',
+    'application/vnd.informix-visionary',
+    'application/vnd.innopath.wamp.notification',
+    'application/vnd.insors.igm',
+    'application/vnd.intercon.formnet',
+    'application/vnd.intergeo',
+    'application/vnd.intertrust.digibox',
+    'application/vnd.intertrust.nncp',
+    'application/vnd.intu.qbo',
+    'application/vnd.intu.qfx',
+    'application/vnd.iptc.g2.catalogitem+xml',
+    'application/vnd.iptc.g2.conceptitem+xml',
+    'application/vnd.iptc.g2.knowledgeitem+xml',
+    'application/vnd.iptc.g2.newsitem+xml',
+    'application/vnd.iptc.g2.newsmessage+xml',
+    'application/vnd.iptc.g2.packageitem+xml',
+    'application/vnd.iptc.g2.planningitem+xml',
+    'application/vnd.ipunplugged.rcprofile',
+    'application/vnd.irepository.package+xml',
+    'application/vnd.is-xpr',
+    'application/vnd.isac.fcs',
+    'application/vnd.jam',
+    'application/vnd.iso11783-10+zip',
+    'application/vnd.japannet-directory-service',
+    'application/vnd.japannet-jpnstore-wakeup',
+    'application/vnd.japannet-payment-wakeup',
+    'application/vnd.japannet-registration',
+    'application/vnd.japannet-registration-wakeup',
+    'application/vnd.japannet-setstore-wakeup',
+    'application/vnd.japannet-verification',
+    'application/vnd.japannet-verification-wakeup',
+    'application/vnd.jcp.javame.midlet-rms',
+    'application/vnd.jisp',
+    'application/vnd.joost.joda-archive',
+    'application/vnd.jsk.isdn-ngn',
+    'application/vnd.kahootz',
+    'application/vnd.kde.karbon',
+    'application/vnd.kde.kchart',
+    'application/vnd.kde.kformula',
+    'application/vnd.kde.kivio',
+    'application/vnd.kde.kontour',
+    'application/vnd.kde.kpresenter',
+    'application/vnd.kde.kspread',
+    'application/vnd.kde.kword',
+    'application/vnd.kenameaapp',
+    'application/vnd.kidspiration',
+    'application/vnd.Kinar',
+    'application/vnd.koan',
+    'application/vnd.kodak-descriptor',
+    'application/vnd.las',
+    'application/vnd.las.las+json',
+    'application/vnd.las.las+xml',
+    'application/vnd.laszip',
+    'application/vnd.leap+json',
+    'application/vnd.liberty-request+xml',
+    'application/vnd.llamagraphics.life-balance.desktop',
+    'application/vnd.llamagraphics.life-balance.exchange+xml',
+    'application/vnd.logipipe.circuit+zip',
+    'application/vnd.loom',
+    'application/vnd.lotus-1-2-3',
+    'application/vnd.lotus-approach',
+    'application/vnd.lotus-freelance',
+    'application/vnd.lotus-notes',
+    'application/vnd.lotus-organizer',
+    'application/vnd.lotus-screencam',
+    'application/vnd.lotus-wordpro',
+    'application/vnd.macports.portpkg',
+    'application/vnd.mapbox-vector-tile',
+    'application/vnd.marlin.drm.actiontoken+xml',
+    'application/vnd.marlin.drm.conftoken+xml',
+    'application/vnd.marlin.drm.license+xml',
+    'application/vnd.marlin.drm.mdcf',
+    'application/vnd.mason+json',
+    'application/vnd.maxar.archive.3tz+zip',
+    'application/vnd.maxmind.maxmind-db',
+    'application/vnd.mcd',
+    'application/vnd.medcalcdata',
+    'application/vnd.mediastation.cdkey',
+    'application/vnd.meridian-slingshot',
+    'application/vnd.MFER',
+    'application/vnd.mfmp',
+    'application/vnd.micro+json',
+    'application/vnd.micrografx.flo',
+    'application/vnd.micrografx.igx',
+    'application/vnd.microsoft.portable-executable',
+    'application/vnd.microsoft.windows.thumbnail-cache',
+    'application/vnd.miele+json',
+    'application/vnd.mif',
+    'application/vnd.minisoft-hp3000-save',
+    'application/vnd.mitsubishi.misty-guard.trustweb',
+    'application/vnd.Mobius.DAF',
+    'application/vnd.Mobius.DIS',
+    'application/vnd.Mobius.MBK',
+    'application/vnd.Mobius.MQY',
+    'application/vnd.Mobius.MSL',
+    'application/vnd.Mobius.PLC',
+    'application/vnd.Mobius.TXF',
+    'application/vnd.mophun.application',
+    'application/vnd.mophun.certificate',
+    'application/vnd.motorola.flexsuite',
+    'application/vnd.motorola.flexsuite.adsi',
+    'application/vnd.motorola.flexsuite.fis',
+    'application/vnd.motorola.flexsuite.gotap',
+    'application/vnd.motorola.flexsuite.kmr',
+    'application/vnd.motorola.flexsuite.ttc',
+    'application/vnd.motorola.flexsuite.wem',
+    'application/vnd.motorola.iprm',
+    'application/vnd.mozilla.xul+xml',
+    'application/vnd.ms-artgalry',
+    'application/vnd.ms-asf',
+    'application/vnd.ms-cab-compressed',
+    'application/vnd.ms-3mfdocument',
+    'application/vnd.ms-excel',
+    'application/vnd.ms-excel.addin.macroEnabled.12',
+    'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+    'application/vnd.ms-excel.sheet.macroEnabled.12',
+    'application/vnd.ms-excel.template.macroEnabled.12',
+    'application/vnd.ms-fontobject',
+    'application/vnd.ms-htmlhelp',
+    'application/vnd.ms-ims',
+    'application/vnd.ms-lrm',
+    'application/vnd.ms-office.activeX+xml',
+    'application/vnd.ms-officetheme',
+    'application/vnd.ms-playready.initiator+xml',
+    'application/vnd.ms-powerpoint',
+    'application/vnd.ms-powerpoint.addin.macroEnabled.12',
+    'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+    'application/vnd.ms-powerpoint.slide.macroEnabled.12',
+    'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
+    'application/vnd.ms-powerpoint.template.macroEnabled.12',
+    'application/vnd.ms-PrintDeviceCapabilities+xml',
+    'application/vnd.ms-PrintSchemaTicket+xml',
+    'application/vnd.ms-project',
+    'application/vnd.ms-tnef',
+    'application/vnd.ms-windows.devicepairing',
+    'application/vnd.ms-windows.nwprinting.oob',
+    'application/vnd.ms-windows.printerpairing',
+    'application/vnd.ms-windows.wsd.oob',
+    'application/vnd.ms-wmdrm.lic-chlg-req',
+    'application/vnd.ms-wmdrm.lic-resp',
+    'application/vnd.ms-wmdrm.meter-chlg-req',
+    'application/vnd.ms-wmdrm.meter-resp',
+    'application/vnd.ms-word.document.macroEnabled.12',
+    'application/vnd.ms-word.template.macroEnabled.12',
+    'application/vnd.ms-works',
+    'application/vnd.ms-wpl',
+    'application/vnd.ms-xpsdocument',
+    'application/vnd.msa-disk-image',
+    'application/vnd.mseq',
+    'application/vnd.msign',
+    'application/vnd.multiad.creator',
+    'application/vnd.multiad.creator.cif',
+    'application/vnd.musician',
+    'application/vnd.music-niff',
+    'application/vnd.muvee.style',
+    'application/vnd.mynfc',
+    'application/vnd.nacamar.ybrid+json',
+    'application/vnd.ncd.control',
+    'application/vnd.ncd.reference',
+    'application/vnd.nearst.inv+json',
+    'application/vnd.nebumind.line',
+    'application/vnd.nervana',
+    'application/vnd.netfpx',
+    'application/vnd.neurolanguage.nlu',
+    'application/vnd.nimn',
+    'application/vnd.nintendo.snes.rom',
+    'application/vnd.nintendo.nitro.rom',
+    'application/vnd.nitf',
+    'application/vnd.noblenet-directory',
+    'application/vnd.noblenet-sealer',
+    'application/vnd.noblenet-web',
+    'application/vnd.nokia.catalogs',
+    'application/vnd.nokia.conml+wbxml',
+    'application/vnd.nokia.conml+xml',
+    'application/vnd.nokia.iptv.config+xml',
+    'application/vnd.nokia.iSDS-radio-presets',
+    'application/vnd.nokia.landmark+wbxml',
+    'application/vnd.nokia.landmark+xml',
+    'application/vnd.nokia.landmarkcollection+xml',
+    'application/vnd.nokia.ncd',
+    'application/vnd.nokia.n-gage.ac+xml',
+    'application/vnd.nokia.n-gage.data',
+    'application/vnd.nokia.n-gage.symbian.install',
+    'application/vnd.nokia.pcd+wbxml',
+    'application/vnd.nokia.pcd+xml',
+    'application/vnd.nokia.radio-preset',
+    'application/vnd.nokia.radio-presets',
+    'application/vnd.novadigm.EDM',
+    'application/vnd.novadigm.EDX',
+    'application/vnd.novadigm.EXT',
+    'application/vnd.ntt-local.content-share',
+    'application/vnd.ntt-local.file-transfer',
+    'application/vnd.ntt-local.ogw_remote-access',
+    'application/vnd.ntt-local.sip-ta_remote',
+    'application/vnd.ntt-local.sip-ta_tcp_stream',
+    'application/vnd.oasis.opendocument.chart',
+    'application/vnd.oasis.opendocument.chart-template',
+    'application/vnd.oasis.opendocument.database',
+    'application/vnd.oasis.opendocument.formula',
+    'application/vnd.oasis.opendocument.formula-template',
+    'application/vnd.oasis.opendocument.graphics',
+    'application/vnd.oasis.opendocument.graphics-template',
+    'application/vnd.oasis.opendocument.image',
+    'application/vnd.oasis.opendocument.image-template',
+    'application/vnd.oasis.opendocument.presentation',
+    'application/vnd.oasis.opendocument.presentation-template',
+    'application/vnd.oasis.opendocument.spreadsheet',
+    'application/vnd.oasis.opendocument.spreadsheet-template',
+    'application/vnd.oasis.opendocument.text',
+    'application/vnd.oasis.opendocument.text-master',
+    'application/vnd.oasis.opendocument.text-template',
+    'application/vnd.oasis.opendocument.text-web',
+    'application/vnd.obn',
+    'application/vnd.ocf+cbor',
+    'application/vnd.oci.image.manifest.v1+json',
+    'application/vnd.oftn.l10n+json',
+    'application/vnd.oipf.contentaccessdownload+xml',
+    'application/vnd.oipf.contentaccessstreaming+xml',
+    'application/vnd.oipf.cspg-hexbinary',
+    'application/vnd.oipf.dae.svg+xml',
+    'application/vnd.oipf.dae.xhtml+xml',
+    'application/vnd.oipf.mippvcontrolmessage+xml',
+    'application/vnd.oipf.pae.gem',
+    'application/vnd.oipf.spdiscovery+xml',
+    'application/vnd.oipf.spdlist+xml',
+    'application/vnd.oipf.ueprofile+xml',
+    'application/vnd.oipf.userprofile+xml',
+    'application/vnd.olpc-sugar',
+    'application/vnd.oma.bcast.associated-procedure-parameter+xml',
+    'application/vnd.oma.bcast.drm-trigger+xml',
+    'application/vnd.oma.bcast.imd+xml',
+    'application/vnd.oma.bcast.ltkm',
+    'application/vnd.oma.bcast.notification+xml',
+    'application/vnd.oma.bcast.provisioningtrigger',
+    'application/vnd.oma.bcast.sgboot',
+    'application/vnd.oma.bcast.sgdd+xml',
+    'application/vnd.oma.bcast.sgdu',
+    'application/vnd.oma.bcast.simple-symbol-container',
+    'application/vnd.oma.bcast.smartcard-trigger+xml',
+    'application/vnd.oma.bcast.sprov+xml',
+    'application/vnd.oma.bcast.stkm',
+    'application/vnd.oma.cab-address-book+xml',
+    'application/vnd.oma.cab-feature-handler+xml',
+    'application/vnd.oma.cab-pcc+xml',
+    'application/vnd.oma.cab-subs-invite+xml',
+    'application/vnd.oma.cab-user-prefs+xml',
+    'application/vnd.oma.dcd',
+    'application/vnd.oma.dcdc',
+    'application/vnd.oma.dd2+xml',
+    'application/vnd.oma.drm.risd+xml',
+    'application/vnd.oma.group-usage-list+xml',
+    'application/vnd.oma.lwm2m+cbor',
+    'application/vnd.oma.lwm2m+json',
+    'application/vnd.oma.lwm2m+tlv',
+    'application/vnd.oma.pal+xml',
+    'application/vnd.oma.poc.detailed-progress-report+xml',
+    'application/vnd.oma.poc.final-report+xml',
+    'application/vnd.oma.poc.groups+xml',
+    'application/vnd.oma.poc.invocation-descriptor+xml',
+    'application/vnd.oma.poc.optimized-progress-report+xml',
+    'application/vnd.oma.push',
+    'application/vnd.oma.scidm.messages+xml',
+    'application/vnd.oma.xcap-directory+xml',
+    'application/vnd.omads-email+xml',
+    'application/vnd.omads-file+xml',
+    'application/vnd.omads-folder+xml',
+    'application/vnd.omaloc-supl-init',
+    'application/vnd.oma-scws-config',
+    'application/vnd.oma-scws-http-request',
+    'application/vnd.oma-scws-http-response',
+    'application/vnd.onepager',
+    'application/vnd.onepagertamp',
+    'application/vnd.onepagertamx',
+    'application/vnd.onepagertat',
+    'application/vnd.onepagertatp',
+    'application/vnd.onepagertatx',
+    'application/vnd.openblox.game-binary',
+    'application/vnd.openblox.game+xml',
+    'application/vnd.openeye.oeb',
+    'application/vnd.openstreetmap.data+xml',
+    'application/vnd.opentimestamps.ots',
+    'application/vnd.openxmlformats-officedocument.custom-properties+xml',
+    'application/vnd.openxmlformats-officedocument.customXmlProperties+xml',
+    'application/vnd.openxmlformats-officedocument.drawing+xml',
+    'application/vnd.openxmlformats-officedocument.drawingml.chart+xml',
+    'application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml',
+    'application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml',
+    'application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml',
+    'application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml',
+    'application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml',
+    'application/vnd.openxmlformats-officedocument.extended-properties+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.comments+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+    'application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.presProps+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.slide',
+    'application/vnd.openxmlformats-officedocument.presentationml.slide+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+    'application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.tags+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.template',
+    'application/vnd.openxmlformats-officedocument.presentationml.template.main+xml',
+    'application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml',
+    'application/vnd.openxmlformats-officedocument.theme+xml',
+    'application/vnd.openxmlformats-officedocument.themeOverride+xml',
+    'application/vnd.openxmlformats-officedocument.vmlDrawing',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml',
+    'application/vnd.openxmlformats-package.core-properties+xml',
+    'application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml',
+    'application/vnd.openxmlformats-package.relationships+xml',
+    'application/vnd.oracle.resource+json',
+    'application/vnd.orange.indata',
+    'application/vnd.osa.netdeploy',
+    'application/vnd.osgeo.mapguide.package',
+    'application/vnd.osgi.bundle',
+    'application/vnd.osgi.dp',
+    'application/vnd.osgi.subsystem',
+    'application/vnd.otps.ct-kip+xml',
+    'application/vnd.oxli.countgraph',
+    'application/vnd.pagerduty+json',
+    'application/vnd.palm',
+    'application/vnd.panoply',
+    'application/vnd.paos.xml',
+    'application/vnd.patentdive',
+    'application/vnd.patientecommsdoc',
+    'application/vnd.pawaafile',
+    'application/vnd.pcos',
+    'application/vnd.pg.format',
+    'application/vnd.pg.osasli',
+    'application/vnd.piaccess.application-licence',
+    'application/vnd.picsel',
+    'application/vnd.pmi.widget',
+    'application/vnd.poc.group-advertisement+xml',
+    'application/vnd.pocketlearn',
+    'application/vnd.powerbuilder6',
+    'application/vnd.powerbuilder6-s',
+    'application/vnd.powerbuilder7',
+    'application/vnd.powerbuilder75',
+    'application/vnd.powerbuilder75-s',
+    'application/vnd.powerbuilder7-s',
+    'application/vnd.preminet',
+    'application/vnd.previewsystems.box',
+    'application/vnd.proteus.magazine',
+    'application/vnd.psfs',
+    'application/vnd.publishare-delta-tree',
+    'application/vnd.pvi.ptid1',
+    'application/vnd.pwg-multiplexed',
+    'application/vnd.pwg-xhtml-print+xml',
+    'application/vnd.qualcomm.brew-app-res',
+    'application/vnd.quarantainenet',
+    'application/vnd.Quark.QuarkXPress',
+    'application/vnd.quobject-quoxdocument',
+    'application/vnd.radisys.moml+xml',
+    'application/vnd.radisys.msml-audit-conf+xml',
+    'application/vnd.radisys.msml-audit-conn+xml',
+    'application/vnd.radisys.msml-audit-dialog+xml',
+    'application/vnd.radisys.msml-audit-stream+xml',
+    'application/vnd.radisys.msml-audit+xml',
+    'application/vnd.radisys.msml-conf+xml',
+    'application/vnd.radisys.msml-dialog-base+xml',
+    'application/vnd.radisys.msml-dialog-fax-detect+xml',
+    'application/vnd.radisys.msml-dialog-fax-sendrecv+xml',
+    'application/vnd.radisys.msml-dialog-group+xml',
+    'application/vnd.radisys.msml-dialog-speech+xml',
+    'application/vnd.radisys.msml-dialog-transform+xml',
+    'application/vnd.radisys.msml-dialog+xml',
+    'application/vnd.radisys.msml+xml',
+    'application/vnd.rainstor.data',
+    'application/vnd.rapid',
+    'application/vnd.rar',
+    'application/vnd.realvnc.bed',
+    'application/vnd.recordare.musicxml',
+    'application/vnd.recordare.musicxml+xml',
+    'application/vnd.RenLearn.rlprint',
+    'application/vnd.resilient.logic',
+    'application/vnd.restful+json',
+    'application/vnd.rig.cryptonote',
+    'application/vnd.route66.link66+xml',
+    'application/vnd.rs-274x',
+    'application/vnd.ruckus.download',
+    'application/vnd.s3sms',
+    'application/vnd.sailingtracker.track',
+    'application/vnd.sar',
+    'application/vnd.sbm.cid',
+    'application/vnd.sbm.mid2',
+    'application/vnd.scribus',
+    'application/vnd.sealed.3df',
+    'application/vnd.sealed.csf',
+    'application/vnd.sealed.doc',
+    'application/vnd.sealed.eml',
+    'application/vnd.sealed.mht',
+    'application/vnd.sealed.net',
+    'application/vnd.sealed.ppt',
+    'application/vnd.sealed.tiff',
+    'application/vnd.sealed.xls',
+    'application/vnd.sealedmedia.softseal.html',
+    'application/vnd.sealedmedia.softseal.pdf',
+    'application/vnd.seemail',
+    'application/vnd.seis+json',
+    'application/vnd.sema',
+    'application/vnd.semd',
+    'application/vnd.semf',
+    'application/vnd.shade-save-file',
+    'application/vnd.shana.informed.formdata',
+    'application/vnd.shana.informed.formtemplate',
+    'application/vnd.shana.informed.interchange',
+    'application/vnd.shana.informed.package',
+    'application/vnd.shootproof+json',
+    'application/vnd.shopkick+json',
+    'application/vnd.shp',
+    'application/vnd.shx',
+    'application/vnd.sigrok.session',
+    'application/vnd.SimTech-MindMapper',
+    'application/vnd.siren+json',
+    'application/vnd.smaf',
+    'application/vnd.smart.notebook',
+    'application/vnd.smart.teacher',
+    'application/vnd.snesdev-page-table',
+    'application/vnd.software602.filler.form+xml',
+    'application/vnd.software602.filler.form-xml-zip',
+    'application/vnd.solent.sdkm+xml',
+    'application/vnd.spotfire.dxp',
+    'application/vnd.spotfire.sfs',
+    'application/vnd.sqlite3',
+    'application/vnd.sss-cod',
+    'application/vnd.sss-dtf',
+    'application/vnd.sss-ntf',
+    'application/vnd.stepmania.package',
+    'application/vnd.stepmania.stepchart',
+    'application/vnd.street-stream',
+    'application/vnd.sun.wadl+xml',
+    'application/vnd.sus-calendar',
+    'application/vnd.svd',
+    'application/vnd.swiftview-ics',
+    'application/vnd.sycle+xml',
+    'application/vnd.syft+json',
+    'application/vnd.syncml.dm.notification',
+    'application/vnd.syncml.dmddf+xml',
+    'application/vnd.syncml.dmtnds+wbxml',
+    'application/vnd.syncml.dmtnds+xml',
+    'application/vnd.syncml.dmddf+wbxml',
+    'application/vnd.syncml.dm+wbxml',
+    'application/vnd.syncml.dm+xml',
+    'application/vnd.syncml.ds.notification',
+    'application/vnd.syncml+xml',
+    'application/vnd.tableschema+json',
+    'application/vnd.tao.intent-module-archive',
+    'application/vnd.tcpdump.pcap',
+    'application/vnd.think-cell.ppttc+json',
+    'application/vnd.tml',
+    'application/vnd.tmd.mediaflex.api+xml',
+    'application/vnd.tmobile-livetv',
+    'application/vnd.tri.onesource',
+    'application/vnd.trid.tpt',
+    'application/vnd.triscape.mxs',
+    'application/vnd.trueapp',
+    'application/vnd.truedoc',
+    'application/vnd.ubisoft.webplayer',
+    'application/vnd.ufdl',
+    'application/vnd.uiq.theme',
+    'application/vnd.umajin',
+    'application/vnd.unity',
+    'application/vnd.uoml+xml',
+    'application/vnd.uplanet.alert',
+    'application/vnd.uplanet.alert-wbxml',
+    'application/vnd.uplanet.bearer-choice',
+    'application/vnd.uplanet.bearer-choice-wbxml',
+    'application/vnd.uplanet.cacheop',
+    'application/vnd.uplanet.cacheop-wbxml',
+    'application/vnd.uplanet.channel',
+    'application/vnd.uplanet.channel-wbxml',
+    'application/vnd.uplanet.list',
+    'application/vnd.uplanet.listcmd',
+    'application/vnd.uplanet.listcmd-wbxml',
+    'application/vnd.uplanet.list-wbxml',
+    'application/vnd.uri-map',
+    'application/vnd.uplanet.signal',
+    'application/vnd.valve.source.material',
+    'application/vnd.vcx',
+    'application/vnd.vd-study',
+    'application/vnd.vectorworks',
+    'application/vnd.vel+json',
+    'application/vnd.verimatrix.vcas',
+    'application/vnd.veritone.aion+json',
+    'application/vnd.veryant.thin',
+    'application/vnd.ves.encrypted',
+    'application/vnd.vidsoft.vidconference',
+    'application/vnd.visio',
+    'application/vnd.visionary',
+    'application/vnd.vividence.scriptfile',
+    'application/vnd.vsf',
+    'application/vnd.wap.sic',
+    'application/vnd.wap.slc',
+    'application/vnd.wap.wbxml',
+    'application/vnd.wap.wmlc',
+    'application/vnd.wap.wmlscriptc',
+    'application/vnd.webturbo',
+    'application/vnd.wfa.dpp',
+    'application/vnd.wfa.p2p',
+    'application/vnd.wfa.wsc',
+    'application/vnd.windows.devicepairing',
+    'application/vnd.wmc',
+    'application/vnd.wmf.bootstrap',
+    'application/vnd.wolfram.mathematica',
+    'application/vnd.wolfram.mathematica.package',
+    'application/vnd.wolfram.player',
+    'application/vnd.wordperfect',
+    'application/vnd.wqd',
+    'application/vnd.wrq-hp3000-labelled',
+    'application/vnd.wt.stf',
+    'application/vnd.wv.csp+xml',
+    'application/vnd.wv.csp+wbxml',
+    'application/vnd.wv.ssp+xml',
+    'application/vnd.xacml+json',
+    'application/vnd.xara',
+    'application/vnd.xfdl',
+    'application/vnd.xfdl.webform',
+    'application/vnd.xmi+xml',
+    'application/vnd.xmpie.cpkg',
+    'application/vnd.xmpie.dpkg',
+    'application/vnd.xmpie.plan',
+    'application/vnd.xmpie.ppkg',
+    'application/vnd.xmpie.xlim',
+    'application/vnd.yamaha.hv-dic',
+    'application/vnd.yamaha.hv-script',
+    'application/vnd.yamaha.hv-voice',
+    'application/vnd.yamaha.openscoreformat.osfpvg+xml',
+    'application/vnd.yamaha.openscoreformat',
+    'application/vnd.yamaha.remote-setup',
+    'application/vnd.yamaha.smaf-audio',
+    'application/vnd.yamaha.smaf-phrase',
+    'application/vnd.yamaha.through-ngn',
+    'application/vnd.yamaha.tunnel-udpencap',
+    'application/vnd.yaoweme',
+    'application/vnd.yellowriver-custom-menu',
+    'application/vnd.youtube.yt',
+    'application/vnd.zul',
+    'application/vnd.zzazz.deck+xml',
+    'application/voicexml+xml',
+    'application/voucher-cms+json',
+    'application/vq-rtcpxr',
+    'application/wasm',
+    'application/watcherinfo+xml',
+    'application/webpush-options+json',
+    'application/whoispp-query',
+    'application/whoispp-response',
+    'application/widget',
+    'application/wita',
+    'application/wordperfect5.1',
+    'application/wsdl+xml',
+    'application/wspolicy+xml',
+    'application/x-pki-message',
+    'application/x-www-form-urlencoded',
+    'application/x-x509-ca-cert',
+    'application/x-x509-ca-ra-cert',
+    'application/x-x509-next-ca-cert',
+    'application/x400-bp',
+    'application/xacml+xml',
+    'application/xcap-att+xml',
+    'application/xcap-caps+xml',
+    'application/xcap-diff+xml',
+    'application/xcap-el+xml',
+    'application/xcap-error+xml',
+    'application/xcap-ns+xml',
+    'application/xcon-conference-info-diff+xml',
+    'application/xcon-conference-info+xml',
+    'application/xenc+xml',
+    'application/xhtml+xml',
+    'application/xliff+xml',
+    'application/xml',
+    'application/xml-dtd',
+    'application/xml-external-parsed-entity',
+    'application/xml-patch+xml',
+    'application/xmpp+xml',
+    'application/xop+xml',
+    'application/xslt+xml',
+    'application/xv+xml',
+    'application/yang',
+    'application/yang-data+json',
+    'application/yang-data+xml',
+    'application/yang-patch+json',
+    'application/yang-patch+xml',
+    'application/yin+xml',
+    'application/zip',
+    'application/zlib',
+    'application/zstd'
+];
diff --git a/karavan-app/src/main/webapp/src/eip/EipCard.tsx b/karavan-app/src/main/webui/src/eip/EipCard.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/eip/EipCard.tsx
rename to karavan-app/src/main/webui/src/eip/EipCard.tsx
diff --git a/karavan-app/src/main/webapp/src/eip/EipModal.tsx b/karavan-app/src/main/webui/src/eip/EipModal.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/eip/EipModal.tsx
rename to karavan-app/src/main/webui/src/eip/EipModal.tsx
diff --git a/karavan-app/src/main/webapp/src/eip/EipPage.tsx b/karavan-app/src/main/webui/src/eip/EipPage.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/eip/EipPage.tsx
rename to karavan-app/src/main/webui/src/eip/EipPage.tsx
diff --git a/karavan-app/src/main/webapp/src/index.css b/karavan-app/src/main/webui/src/index.css
similarity index 100%
rename from karavan-app/src/main/webapp/src/index.css
rename to karavan-app/src/main/webui/src/index.css
diff --git a/karavan-app/src/main/webapp/src/index.tsx b/karavan-app/src/main/webui/src/index.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/index.tsx
rename to karavan-app/src/main/webui/src/index.tsx
diff --git a/karavan-app/src/main/webapp/src/kamelets/KameletCard.tsx b/karavan-app/src/main/webui/src/kamelets/KameletCard.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/kamelets/KameletCard.tsx
rename to karavan-app/src/main/webui/src/kamelets/KameletCard.tsx
diff --git a/karavan-app/src/main/webapp/src/kamelets/KameletModal.tsx b/karavan-app/src/main/webui/src/kamelets/KameletModal.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/kamelets/KameletModal.tsx
rename to karavan-app/src/main/webui/src/kamelets/KameletModal.tsx
diff --git a/karavan-app/src/main/webapp/src/kamelets/KameletsPage.tsx b/karavan-app/src/main/webui/src/kamelets/KameletsPage.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/kamelets/KameletsPage.tsx
rename to karavan-app/src/main/webui/src/kamelets/KameletsPage.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/CreateFileModal.tsx b/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/CreateFileModal.tsx
rename to karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx b/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
rename to karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectModels.ts b/karavan-app/src/main/webui/src/projects/ProjectModels.ts
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/ProjectModels.ts
rename to karavan-app/src/main/webui/src/projects/ProjectModels.ts
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectOperations.tsx b/karavan-app/src/main/webui/src/projects/ProjectOperations.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/ProjectOperations.tsx
rename to karavan-app/src/main/webui/src/projects/ProjectOperations.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
rename to karavan-app/src/main/webui/src/projects/ProjectPage.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPageToolbar.tsx b/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/ProjectPageToolbar.tsx
rename to karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
rename to karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/PropertiesEditor.tsx b/karavan-app/src/main/webui/src/projects/PropertiesEditor.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/PropertiesEditor.tsx
rename to karavan-app/src/main/webui/src/projects/PropertiesEditor.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx b/karavan-app/src/main/webui/src/projects/PropertiesTable.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx
rename to karavan-app/src/main/webui/src/projects/PropertiesTable.tsx
diff --git a/karavan-app/src/main/webapp/src/projects/UploadModal.tsx b/karavan-app/src/main/webui/src/projects/UploadModal.tsx
similarity index 100%
rename from karavan-app/src/main/webapp/src/projects/UploadModal.tsx
rename to karavan-app/src/main/webui/src/projects/UploadModal.tsx
diff --git a/karavan-app/src/main/webapp/src/react-app-env.d.ts b/karavan-app/src/main/webui/src/react-app-env.d.ts
similarity index 100%
rename from karavan-app/src/main/webapp/src/react-app-env.d.ts
rename to karavan-app/src/main/webui/src/react-app-env.d.ts
diff --git a/karavan-app/src/main/webapp/tsconfig.json b/karavan-app/src/main/webui/tsconfig.json
similarity index 100%
rename from karavan-app/src/main/webapp/tsconfig.json
rename to karavan-app/src/main/webui/tsconfig.json