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 2023/08/01 20:54:05 UTC

[camel-karavan] branch main updated: Camel-main runtme in web #839

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


The following commit(s) were added to refs/heads/main by this push:
     new 6ea47dff Camel-main runtme in web #839
6ea47dff is described below

commit 6ea47dff82203dca900e84dc72bb91b8d40d8191
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Tue Aug 1 16:53:55 2023 -0400

    Camel-main runtme in web #839
---
 .../apache/camel/karavan/api/ProjectResource.java  |   4 +-
 .../apache/camel/karavan/service/CodeService.java  |  21 ++-
 .../src/main/resources/application.properties      |   4 +-
 .../camel-main-docker-application.properties       |  14 ++
 .../camel-main-kubernetes-application.properties   |  19 ++
 .../camel-main-openshift-application.properties    |  20 ++
 ...-main-org.apache.camel.AggregationStrategy.java |  19 ++
 .../camel-main-org.apache.camel.Processor.java     |  11 ++
 ...rties => quarkus-docker-application.properties} |   9 +-
 .../quarkus-kubernetes-application.properties      |   2 +-
 .../quarkus-openshift-application.properties       |   2 +-
 .../spring-boot-docker-application.properties      |  15 ++
 .../karavan-app/src/main/webui/package-lock.json   |  16 +-
 .../karavan-app/src/main/webui/package.json        |   2 +-
 .../main/webui/src/designer/utils/KaravanIcons.tsx | 210 ++++++++++++++++-----
 .../karavan-app/src/main/webui/src/index.css       |  23 ++-
 .../main/webui/src/projects/CreateProjectModal.tsx |  55 +++---
 .../src/main/webui/src/projects/ProjectsPage.tsx   |   2 +-
 .../main/webui/src/projects/ProjectsTableRow.tsx   |  20 +-
 .../karavan-app/src/main/webui/tsconfig.json       |  12 +-
 20 files changed, 365 insertions(+), 115 deletions(-)

diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
index 262478cd..cc73eae9 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
@@ -69,7 +69,7 @@ public class ProjectResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
     public Project save(Project project) throws Exception {
-        boolean isNew = infinispanService.getProject(project.getProjectId()) != null;
+        boolean isNew = infinispanService.getProject(project.getProjectId()) == null;
         infinispanService.saveProject(project);
         if (isNew){
             ProjectFile appProp = codeService.getApplicationProperties(project);
@@ -83,7 +83,7 @@ public class ProjectResource {
     @Path("/{project}")
     public void delete(@HeaderParam("username") String username,
                           @PathParam("project") String project) throws Exception {
-        String projectId = URLDecoder.decode(project, StandardCharsets.UTF_8.toString());
+        String projectId = URLDecoder.decode(project, StandardCharsets.UTF_8);
         gitService.deleteProject(projectId, infinispanService.getProjectFiles(projectId));
         infinispanService.getProjectFiles(projectId).forEach(file -> infinispanService.deleteProjectFile(projectId, file.getName()));
         infinispanService.deleteProject(projectId);
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java
index f1125b92..85c80464 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java
@@ -22,6 +22,7 @@ import io.apicurio.datamodels.Library;
 import io.apicurio.datamodels.openapi.models.OasDocument;
 import io.quarkus.qute.Engine;
 import io.quarkus.qute.Template;
+import io.quarkus.qute.TemplateInstance;
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
 import org.apache.camel.CamelContext;
@@ -34,6 +35,7 @@ import org.apache.camel.karavan.infinispan.model.GitRepoFile;
 import org.apache.camel.karavan.infinispan.model.Project;
 import org.apache.camel.karavan.infinispan.model.ProjectFile;
 import org.apache.camel.karavan.kubernetes.KubernetesService;
+import org.apache.camel.karavan.shared.ConfigService;
 import org.jboss.logging.Logger;
 import org.yaml.snakeyaml.LoaderOptions;
 import org.yaml.snakeyaml.Yaml;
@@ -63,8 +65,8 @@ public class CodeService {
     @Inject
     Engine engine;
 
-    List<String> runtimes = List.of("quarkus", "spring-boot");
-    List<String> targets = List.of("openshift", "kubernetes");
+    List<String> runtimes = List.of("quarkus", "spring-boot", "camel-main");
+    List<String> targets = List.of("openshift", "kubernetes", "docker");
     List<String> interfaces = List.of("org.apache.camel.AggregationStrategy.java", "org.apache.camel.Processor.java");
 
     public static final Map<String, String> DEFAULT_CONTAINER_RESOURCES = Map.of(
@@ -75,16 +77,21 @@ public class CodeService {
     );
 
     public ProjectFile getApplicationProperties(Project project) {
-        String target = kubernetesService.isOpenshift() ? "openshift" : "kubernetes";
+        String target = "docker";
+        if (ConfigService.inKubernetes()) {
+            target = kubernetesService.isOpenshift() ? "openshift" : "kubernetes";
+        }
         String templateName = project.getRuntime() + "-" + target + "-" + APPLICATION_PROPERTIES_FILENAME;
         String templateText = getTemplateText(templateName);
         Template result = engine.parse(templateText);
-        String code =  result
+        TemplateInstance instance = result
                 .data("projectId", project.getProjectId())
                 .data("projectName", project.getName())
-                .data("projectDescription", project.getDescription())
-                .data("namespace", kubernetesService.getNamespace())
-                .render();
+                .data("projectDescription", project.getDescription());
+        if (ConfigService.inKubernetes()) {
+            instance.data("namespace", kubernetesService.getNamespace());
+        }
+        String code =  result.render();
         return new ProjectFile(APPLICATION_PROPERTIES_FILENAME, code, project.getProjectId(), Instant.now().toEpochMilli());
     }
 
diff --git a/karavan-web/karavan-app/src/main/resources/application.properties b/karavan-web/karavan-app/src/main/resources/application.properties
index b726e933..002a0682 100644
--- a/karavan-web/karavan-app/src/main/resources/application.properties
+++ b/karavan-web/karavan-app/src/main/resources/application.properties
@@ -1,8 +1,8 @@
 karavan.version=4.0.0-RC2
 karavan.environment=dev
 karavan.environments=dev
-karavan.default-runtime=quarkus
-karavan.runtimes=quarkus,spring-boot
+karavan.default-runtime=camel-main
+karavan.runtimes=camel-main,quarkus,spring-boot
 karavan.camel.status.interval=off
 karavan.container.status.interval=3s
 karavan.container.infinispan.interval=5s
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-docker-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-docker-application.properties
new file mode 100644
index 00000000..381727c1
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-docker-application.properties
@@ -0,0 +1,14 @@
+camel.karavan.project-id={projectId}
+camel.karavan.project-name={projectName}
+camel.karavan.project-description={projectDescription}
+camel.jbang.gav=org.camel.karavan.demo:{projectId}:1
+camel.jbang.runtime=camel-main
+camel.jbang.version=4.0.0-RC2
+camel.jbang.dependencies=camel:microprofile-health
+camel.health.enabled=true
+camel.health.exposure-level=full
+management.endpoints.web.exposure.include=health
+management.health.probes.enabled=true
+management.health.livenessState.enabled=true
+management.health.readinessState.enabled=true
+management.endpoint.health.show-details=always
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-kubernetes-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-kubernetes-application.properties
new file mode 100644
index 00000000..02fc81d8
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-kubernetes-application.properties
@@ -0,0 +1,19 @@
+camel.karavan.project-id={projectId}
+camel.karavan.project-name={projectName}
+camel.karavan.project-description={projectDescription}
+camel.jbang.gav=org.camel.karavan.demo:{projectId}:1
+camel.jbang.runtime=camel-main
+camel.jbang.version=4.0.0-RC2
+camel.jbang.dependencies=camel:microprofile-health
+camel.health.enabled=true
+camel.health.exposure-level=full
+management.endpoints.web.exposure.include=health
+management.health.probes.enabled=true
+management.health.livenessState.enabled=true
+management.health.readinessState.enabled=true
+management.endpoint.health.show-details=always
+jkube.version=1.13.1
+jkube.build.strategy=jib
+jkube.imagePullPolicy=IfNotPresent
+jkube.enricher.jkube-controller.replicaCount=1
+jkube.enricher.jkube-service.port=80
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-openshift-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-openshift-application.properties
new file mode 100644
index 00000000..3d558bd9
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-openshift-application.properties
@@ -0,0 +1,20 @@
+camel.karavan.project-id={projectId}
+camel.karavan.project-name={projectName}
+camel.karavan.project-description={projectDescription}
+camel.jbang.gav=org.camel.karavan.demo:{projectId}:1
+camel.jbang.runtime=camel-main
+camel.jbang.version=4.0.0-RC2
+camel.jbang.dependencies=camel:microprofile-health
+camel.health.enabled=true
+camel.health.exposure-level=full
+management.endpoints.web.exposure.include=health
+management.health.probes.enabled=true
+management.health.livenessState.enabled=true
+management.health.readinessState.enabled=true
+management.endpoint.health.show-details=always
+jkube.version=1.13.1
+jkube.build.strategy=jib
+jkube.imagePullPolicy=IfNotPresent
+jkube.enricher.jkube-controller.type=Deployment
+jkube.enricher.jkube-controller.replicaCount=1
+jkube.enricher.jkube-service.port=80
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-org.apache.camel.AggregationStrategy.java b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-org.apache.camel.AggregationStrategy.java
new file mode 100644
index 00000000..094ee279
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-org.apache.camel.AggregationStrategy.java
@@ -0,0 +1,19 @@
+import org.apache.camel.AggregationStrategy;
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+
+@BindToRegistry("NAME")
+public class NAME implements AggregationStrategy {
+    @Override
+    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+
+        if (oldExchange == null) {
+            return newExchange;
+        }
+
+        String oldBody = oldExchange.getIn().getBody(String.class);
+        String newBody = newExchange.getIn().getBody(String.class);
+        oldExchange.getIn().setBody(oldBody + "+" + newBody);
+        return oldExchange;
+    }
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-org.apache.camel.Processor.java b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-org.apache.camel.Processor.java
new file mode 100644
index 00000000..66f39afd
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-org.apache.camel.Processor.java
@@ -0,0 +1,11 @@
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+
+@BindToRegistry("NAME")
+public class NAME implements Processor {
+
+  public void process(Exchange exchange) throws Exception {
+      exchange.getIn().setBody("Hello World");
+  }
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/quarkus-kubernetes-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/quarkus-docker-application.properties
similarity index 53%
copy from karavan-web/karavan-app/src/main/resources/snippets/quarkus-kubernetes-application.properties
copy to karavan-web/karavan-app/src/main/resources/snippets/quarkus-docker-application.properties
index 067be469..83fe4779 100644
--- a/karavan-web/karavan-app/src/main/resources/snippets/quarkus-kubernetes-application.properties
+++ b/karavan-web/karavan-app/src/main/resources/snippets/quarkus-docker-application.properties
@@ -3,13 +3,8 @@ camel.karavan.project-name={projectName}
 camel.karavan.project-description={projectDescription}
 camel.jbang.gav=org.camel.karavan.demo:{projectId}:1
 camel.jbang.runtime=quarkus
-camel.jbang.quarkusVersion=2.16.7.Final
-camel.jbang.dependencies=camel:microprofile-health,mvn:io.quarkus:quarkus-kubernetes,mvn:io.quarkus:quarkus-container-image-jib
+camel.jbang.quarkusVersion=3.2.2.Final
+camel.jbang.dependencies=camel:microprofile-health,mvn:io.quarkus:quarkus-container-image-jib
 camel.health.enabled=true
 camel.health.exposure-level=full
 quarkus.container-image.name={projectId}
-quarkus.kubernetes.replicas=1
-quarkus.kubernetes.resources.requests.memory=64Mi
-quarkus.kubernetes.resources.requests.cpu=250m
-quarkus.kubernetes.resources.limits.memory=512Mi
-quarkus.kubernetes.resources.limits.cpu=1000m
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/quarkus-kubernetes-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/quarkus-kubernetes-application.properties
index 067be469..e68ecafb 100644
--- a/karavan-web/karavan-app/src/main/resources/snippets/quarkus-kubernetes-application.properties
+++ b/karavan-web/karavan-app/src/main/resources/snippets/quarkus-kubernetes-application.properties
@@ -3,7 +3,7 @@ camel.karavan.project-name={projectName}
 camel.karavan.project-description={projectDescription}
 camel.jbang.gav=org.camel.karavan.demo:{projectId}:1
 camel.jbang.runtime=quarkus
-camel.jbang.quarkusVersion=2.16.7.Final
+camel.jbang.quarkusVersion=3.2.2.Final
 camel.jbang.dependencies=camel:microprofile-health,mvn:io.quarkus:quarkus-kubernetes,mvn:io.quarkus:quarkus-container-image-jib
 camel.health.enabled=true
 camel.health.exposure-level=full
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/quarkus-openshift-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/quarkus-openshift-application.properties
index 7d322ec0..c59944c5 100644
--- a/karavan-web/karavan-app/src/main/resources/snippets/quarkus-openshift-application.properties
+++ b/karavan-web/karavan-app/src/main/resources/snippets/quarkus-openshift-application.properties
@@ -3,7 +3,7 @@ camel.karavan.project-name={projectName}
 camel.karavan.project-description={projectDescription}
 camel.jbang.gav=org.camel.karavan.demo:{projectId}:1
 camel.jbang.runtime=quarkus
-camel.jbang.quarkusVersion=2.16.7.Final
+camel.jbang.quarkusVersion=3.2.2.Final
 camel.jbang.dependencies=camel:microprofile-health,mvn:io.quarkus:quarkus-openshift,mvn:io.quarkus:quarkus-container-image-jib
 camel.health.enabled=true
 camel.health.exposure-level=full
diff --git a/karavan-web/karavan-app/src/main/resources/snippets/spring-boot-docker-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/spring-boot-docker-application.properties
new file mode 100644
index 00000000..5003d8fc
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/resources/snippets/spring-boot-docker-application.properties
@@ -0,0 +1,15 @@
+camel.karavan.project-id={projectId}
+camel.karavan.project-name={projectName}
+camel.karavan.project-description={projectDescription}
+camel.jbang.gav=org.camel.karavan.demo:{projectId}:1
+camel.jbang.runtime=spring-boot
+camel.jbang.camelSpringBootVersion=4.0.0-RC2
+camel.jbang.springBootVersion=3.1.2
+camel.jbang.dependencies=camel:microprofile-health
+camel.health.enabled=true
+camel.health.exposure-level=full
+management.endpoints.web.exposure.include=health
+management.health.probes.enabled=true
+management.health.livenessState.enabled=true
+management.health.readinessState.enabled=true
+management.endpoint.health.show-details=always
diff --git a/karavan-web/karavan-app/src/main/webui/package-lock.json b/karavan-web/karavan-app/src/main/webui/package-lock.json
index a471933c..e3955924 100644
--- a/karavan-web/karavan-app/src/main/webui/package-lock.json
+++ b/karavan-web/karavan-app/src/main/webui/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "karavan",
-  "version": "3.21.1-SNAPSHOT",
+  "version": "4.0.0-RC2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "karavan",
-      "version": "3.21.1-SNAPSHOT",
+      "version": "4.0.0-RC2",
       "dependencies": {
         "@microsoft/fetch-event-source": "^2.0.1",
         "@monaco-editor/react": "4.5.0",
@@ -43,11 +43,11 @@
         "eslint": "^8.33.0",
         "monaco-editor": "0.36.1",
         "react-scripts": "^5.0.1",
-        "typescript": "^4.9.5"
+        "typescript": "^5.1"
       }
     },
     "../../../../../karavan-core": {
-      "version": "3.21.1-SNAPSHOT",
+      "version": "4.0.0-RC2",
       "license": "Apache-2.0",
       "dependencies": {
         "@types/js-yaml": "^4.0.5",
@@ -17641,16 +17641,16 @@
       }
     },
     "node_modules/typescript": {
-      "version": "4.9.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
+      "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
       "dev": true,
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
       },
       "engines": {
-        "node": ">=4.2.0"
+        "node": ">=14.17"
       }
     },
     "node_modules/unbox-primitive": {
diff --git a/karavan-web/karavan-app/src/main/webui/package.json b/karavan-web/karavan-app/src/main/webui/package.json
index 81399892..3c12b7c9 100644
--- a/karavan-web/karavan-app/src/main/webui/package.json
+++ b/karavan-web/karavan-app/src/main/webui/package.json
@@ -61,7 +61,7 @@
     "eslint": "^8.33.0",
     "monaco-editor": "0.36.1",
     "react-scripts": "^5.0.1",
-    "typescript": "^4.9.5"
+    "typescript": "^5.1"
   },
   "overrides": {
     "@svgr/webpack": "$@svgr/webpack",
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
index 63bb26db..6bd0e15b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
@@ -180,17 +180,102 @@ export function KaravanIcon(className?: string) {
     );
 }
 
+export function CamelIcon(props?: (JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>) | undefined) {
+    return (<svg
+            xmlns="http://www.w3.org/2000/svg"
+            xmlnsXlink="http://www.w3.org/1999/xlink"
+            width={24}
+            height={24}
+            preserveAspectRatio="xMidYMid"
+            viewBox="0 0 256 256"
+            {...props}
+            className="icon">
+            <defs>
+                <linearGradient
+                    id="b"
+                    x1="-12.564%"
+                    x2="101.304%"
+                    y1="108.214%"
+                    y2="-1.964%"
+                >
+                    <stop offset="0%" stopColor="#F69923" />
+                    <stop offset="10.996%" stopColor="#F79A23" />
+                    <stop offset="94.502%" stopColor="#E97826" />
+                </linearGradient>
+                <linearGradient
+                    id="d"
+                    x1="-12.564%"
+                    x2="101.304%"
+                    y1="108.214%"
+                    y2="-1.964%"
+                >
+                    <stop offset="0%" stopColor="#F69923" />
+                    <stop offset="8.048%" stopColor="#F79A23" />
+                    <stop offset="41.874%" stopColor="#E97826" />
+                </linearGradient>
+                <linearGradient
+                    id="e"
+                    x1="74.724%"
+                    x2="6.653%"
+                    y1="-3.059%"
+                    y2="100.066%"
+                >
+                    <stop offset="0%" stopColor="#F6E423" />
+                    <stop offset="41.191%" stopColor="#F79A23" />
+                    <stop offset="73.271%" stopColor="#E97826" />
+                </linearGradient>
+                <circle id="a" cx={128} cy={128} r={128} />
+            </defs>
+            <mask id="c" fill="#fff">
+                <use xlinkHref="#a" />
+            </mask>
+            <circle
+                cx={127.994}
+                cy={127.994}
+                r={123.111}
+                fill="url(#b)"
+                mask="url(#c)"
+            />
+            <path
+                fill="url(#d)"
+                d="M128 256C57.308 256 0 198.692 0 128 0 57.308 57.308 0 128 0c70.692 0 128 57.308 128 128 0 70.692-57.308 128-128 128Zm0-9.768c65.298 0 118.232-52.934 118.232-118.232S193.298 9.768 128 9.768 9.768 62.702 9.768 128 62.702 246.232 128 246.232Z"
+                mask="url(#c)"
+            />
+            <path
+                fill="url(#e)"
+                d="M98.044 75.517c-1.751-.002-3.524.01-5.292.061-2.056.06-4.817.713-8 1.785 53.775 40.834 73.108 114.497 39.875 178.514 1.129.03 2.249.123 3.385.123 60.736 0 111.492-42.323 124.609-99.071-38.542-45.178-90.813-81.314-154.578-81.412Z"
+                mask="url(#c)"
+                opacity={0.75}
+            />
+            <path
+                fill="#28170B"
+                d="M84.752 77.368C66.895 83.378 32.83 104.546.079 132.81c2.487 67.334 57.028 121.313 124.548 123.07 33.233-64.016 13.901-137.68-39.875-178.513Z"
+                mask="url(#c)"
+                opacity={0.75}
+            />
+            <path
+                fill="#FFF"
+                d="M128.747 54.005c-10.985 5.495 0 27.466 0 27.466C95.774 108.954 102.78 155.9 64.312 155.9c-20.97 0-42.242-24.077-64.233-38.828-.283 3.479-.785 6.972-.785 10.524 0 48.095 26.263 89.924 65.42 111.897 10.952-1.38 22.838-4.114 31.05-9.592 43.146-28.765 53.857-83.491 71.487-109.925 10.979-16.492 62.434-15.061 65.906-22.01 5.502-10.991-10.99-27.467-16.491-27.467h-43.958c-3.071 0-7.897-5.456-10.974-5.456h-16.492s-7.307-11.085-13.794-11.526c-.93-.066-1.83.053-2.7.488Z"
+                mask="url(#c)"
+            />
+        </svg>
+    );
+}
+
 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="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"/>
+            <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">
@@ -216,7 +301,8 @@ export function getDesignerIcon(icon: string) {
             <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)"/>
+            <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>
@@ -228,20 +314,27 @@ export function getDesignerIcon(icon: string) {
                 <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)"/>
+            <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">
+        <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"/>
+            <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 (
@@ -250,16 +343,20 @@ export function getDesignerIcon(icon: string) {
                 <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"/>
+            <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 === 'routeConfiguration') return (
         <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32">
             <defs>
                 <style>{".cls-1{fill:none;}"}</style>
             </defs>
-            <path d="M28.83 21.17L25 17.37l.67-.67a1 1 0 000-1.41l-6-6a1 1 0 00-1.41 0l-.79.79-6.76-6.79a1 1 0 00-1.41 0l-4 4-.12.15-4 6a1 1 0 00.12 1.26l3 3a1 1 0 001.42 0L10 13.41l2.09 2.09-4.8 4.79a1 1 0 000 1.41l2 2a1 1 0 00.71.3 1 1 0 00.52-.15l4.33-2.6 2.44 2.45a1 1 0 001.41 0l.67-.7 3.79 3.83a4 4 0 005.66-5.66zM10 10.58l-5 5-1.71-1.71 3.49-5.24L10 5.41l6.09 6.09-2.59 2.58zm8 11l-2.84-2.84-5 3-.74-.74L19 11.41 23.59 16zm9.42 3.83a2 2 0 01-2.83 0l-3.8-3.79 2.83-2.83 3.8 3.79a2 2 0 0 [...]
+            <path
+                d="M28.83 21.17L25 17.37l.67-.67a1 1 0 000-1.41l-6-6a1 1 0 00-1.41 0l-.79.79-6.76-6.79a1 1 0 00-1.41 0l-4 4-.12.15-4 6a1 1 0 00.12 1.26l3 3a1 1 0 001.42 0L10 13.41l2.09 2.09-4.8 4.79a1 1 0 000 1.41l2 2a1 1 0 00.71.3 1 1 0 00.52-.15l4.33-2.6 2.44 2.45a1 1 0 001.41 0l.67-.7 3.79 3.83a4 4 0 005.66-5.66zM10 10.58l-5 5-1.71-1.71 3.49-5.24L10 5.41l6.09 6.09-2.59 2.58zm8 11l-2.84-2.84-5 3-.74-.74L19 11.41 23.59 16zm9.42 3.83a2 2 0 01-2.83 0l-3.8-3.79 2.83-2.83 3.8 3.79a2 2 0 010 [...]
             <path
                 d="M0 0H32V32H0z"
                 className="cls-1"
@@ -286,7 +383,8 @@ export function getDesignerIcon(icon: string) {
             <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)"/>
+            <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32"
+                  height="32" transform="translate(0 32) rotate(-90)"/>
         </svg>)
 }
 
@@ -303,9 +401,12 @@ export class BeanIcon extends React.Component<any> {
                 <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)"/>
+                <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>
@@ -323,10 +424,14 @@ export class DependencyIcon extends React.Component<any> {
                     <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)"/>
+                <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>
@@ -365,7 +470,8 @@ export class ConceptIcon extends React.Component<any> {
                 <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"/>
+                <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1"
+                      width="32" height="32"/>
             </svg>
         )
     }
@@ -374,12 +480,16 @@ export class ConceptIcon extends React.Component<any> {
 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>
+            <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>
+                <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>
     );
@@ -388,7 +498,8 @@ export function AggregateIcon() {
 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 [...]
+            <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.02c1. [...]
         </svg>
     );
 }
@@ -397,12 +508,16 @@ export function ChoiceIcon() {
 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>
+            <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-.42 [...]
+            <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>
+                <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>
     );
@@ -411,7 +526,8 @@ export function SplitIcon() {
 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 [...]
+            <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.72  [...]
         </svg>
     );
 }
@@ -419,8 +535,10 @@ export function SagaIcon() {
 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 [...]
+            <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.09s [...]
         </svg>
     );
 }
@@ -428,7 +546,8 @@ export function TransformIcon() {
 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>
+            <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>
     );
 }
@@ -489,7 +608,8 @@ export function Intercept() {
                 <style>{".cls-1 {    fill: none; }"}</style>
             </defs>
             <path d="M15 4H17V28H15z"></path>
-            <path d="M10 7v18H4V7h6m0-2H4a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2zM28 7v18h-6V7h6m0-2h-6a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2z"></path>
+            <path
+                d="M10 7v18H4V7h6m0-2H4a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2zM28 7v18h-6V7h6m0-2h-6a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2z"></path>
             <path
                 id="_Transparent_Rectangle_"
                 d="M0 0H32V32H0z"
@@ -536,8 +656,10 @@ export function InterceptSendToEndpoint() {
             <defs>
                 <style>{".cls-1 {    fill: none; }"}</style>
             </defs>
-            <path d="M6 30h12a2.002 2.002 0 002-2v-3h-2v3H6V4h12v3h2V4a2.002 2.002 0 00-2-2H6a2.002 2.002 0 00-2 2v24a2.002 2.002 0 002 2z"></path>
-            <path d="M20.586 20.586L24.172 17 10 17 10 15 24.172 15 20.586 11.414 22 10 28 16 22 22 20.586 20.586z"></path>
+            <path
+                d="M6 30h12a2.002 2.002 0 002-2v-3h-2v3H6V4h12v3h2V4a2.002 2.002 0 00-2-2H6a2.002 2.002 0 00-2 2v24a2.002 2.002 0 002 2z"></path>
+            <path
+                d="M20.586 20.586L24.172 17 10 17 10 15 24.172 15 20.586 11.414 22 10 28 16 22 22 20.586 20.586z"></path>
             <path
                 id="_Transparent_Rectangle_"
                 d="M0 0H32V32H0z"
@@ -553,8 +675,8 @@ export function SpringIcon() {
     return (
         <svg
             xmlns="http://www.w3.org/2000/svg"
-            width="32"
-            height="32"
+            width="24"
+            height="24"
             viewBox="0 0 32 32"
             className="icon">
             <g fill="none" fillRule="evenodd">
@@ -572,8 +694,8 @@ export function QuarkusIcon() {
     return (
         <svg
             xmlns="http://www.w3.org/2000/svg"
-            width="257"
-            height="257"
+            width="24"
+            height="24"
             preserveAspectRatio="xMidYMid"
             viewBox="-0.5 0 257 257"
             className="icon">
diff --git a/karavan-web/karavan-app/src/main/webui/src/index.css b/karavan-web/karavan-app/src/main/webui/src/index.css
index 0367351c..b884e05c 100644
--- a/karavan-web/karavan-app/src/main/webui/src/index.css
+++ b/karavan-web/karavan-app/src/main/webui/src/index.css
@@ -1,5 +1,3 @@
-
-
 #root {
   margin: 0;
   height: 100%;
@@ -93,10 +91,20 @@
   font-size: 14px;
 }
 
+.karavan .projects-page .pf-c-table tr {
+  --pf-c-table--cell--FontSize: 14px;
+}
+
+.karavan .projects-page .icon-td {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
 .karavan .projects-page .icon {
-  height: 16px;
-  width: 16px;
-  margin: auto;
+  height: 21px;
+  width: 21px;
+  margin-top: 8px;
+  position: absolute;
 }
 
 .karavan .project-page .badge,
@@ -104,6 +112,7 @@
   font-size: 14px;
   font-weight: 400;
   padding: 4px 8px 4px 8px;
+  min-width: 70px;
 }
 
 .karavan .projects-page .runtime-badge {
@@ -371,10 +380,6 @@
   align-items: center;
 }
 
-.new-project .radio {
-  margin-right: 16px;
-}
-
 .new-project .pf-c-radio__body {
   margin: 0 6px 0 6px;
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
index 7f635a47..7c1546ff 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
@@ -2,14 +2,14 @@ import React, {useState} from 'react';
 import {
     Button, Form, FormGroup,
     Modal,
-    ModalVariant, Radio, TextInput,
+    ModalVariant, Radio, TextInput, ToggleGroup, ToggleGroupItem,
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {useAppConfigStore, useProjectStore} from "../api/ProjectStore";
 import {ProjectService} from "../api/ProjectService";
 import {Project} from "../api/ProjectModels";
-import {QuarkusIcon, SpringIcon} from "../designer/utils/KaravanIcons";
+import {QuarkusIcon, SpringIcon, CamelIcon} from "../designer/utils/KaravanIcons";
 import {CamelUi} from "../designer/utils/CamelUi";
 
 
@@ -18,35 +18,48 @@ export const CreateProjectModal = () => {
     const {project, operation} = useProjectStore();
     const [name, setName] = useState('');
     const [description, setDescription] = useState('');
-    const [runtime, setRuntime] = useState('');
     const [projectId, setProjectId] = useState('');
     const {config} = useAppConfigStore();
+    const [runtime, setRuntime] = useState(config.runtime);
 
-    function cleanValues()  {
+    function cleanValues() {
         setName("");
         setDescription("");
-        setRuntime("");
+        setRuntime(config.runtime);
         setProjectId("");
     }
 
-    function closeModal () {
+    function closeModal() {
         useProjectStore.setState({operation: "none"});
         cleanValues();
     }
 
-    function confirmAndCloseModal () {
+    function confirmAndCloseModal() {
         ProjectService.createProject(new Project({name: name, description: description, runtime: runtime, projectId: projectId}));
         useProjectStore.setState({operation: "none"});
         cleanValues();
     }
 
-    function onKeyDown (event: React.KeyboardEvent<HTMLDivElement>): void {
+    function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>): void {
         if (event.key === 'Enter' && name !== undefined && description !== undefined && projectId !== undefined) {
             confirmAndCloseModal();
         }
     }
 
+    function getIcon(runtime: string) {
+        if (runtime === 'quarkus') return QuarkusIcon();
+        else if (runtime === 'spring-boot') return SpringIcon();
+        else if (runtime === 'camel-main') return CamelIcon();
+    }
+
+    function getTitle(runtime: string) {
+        if (runtime === 'quarkus') return "Quarkus";
+        else if (runtime === 'spring-boot') return "Spring";
+        else if (runtime === 'camel-main') return "Camel";
+    }
+
     const runtimes = config.runtimes;
+    const defaultRuntime = config.runtime;
     const isReady = projectId && name && description && !['templates', 'kamelets'].includes(projectId);
     return (
         <Modal
@@ -80,19 +93,19 @@ export const CreateProjectModal = () => {
                                onChange={e => setProjectId(CamelUi.nameFromTitle(e))}/>
                 </FormGroup>
                 <FormGroup label="Runtime" fieldId="runtime" isRequired>
-                    {runtimes?.map((r: string) => (
-                        <Radio key={r} id={r} name={r} className="radio" aria-label="runtime"
-                               isChecked={r === runtime}
-                               onChange={checked => {
-                                   if (checked) setRuntime(r)
-                               }}
-                               body={
-                                   <div className="runtime-radio">
-                                       {r === 'quarkus' ? QuarkusIcon() : SpringIcon()}
-                                       <div className="runtime-label">{CamelUtil.capitalizeName(r)}</div>
-                                   </div>}
-                        />
-                    ))}
+                    <ToggleGroup>
+                        {runtimes?.map((r: string) => (
+                            <ToggleGroupItem key={r} id={r} name={r}
+                                             aria-label="runtime"
+                                             isSelected={r === runtime}
+                                             text={getTitle(r)}
+                                             icon={getIcon(r)}
+                                             onChange={checked => {
+                                                 if (checked) setRuntime(r)
+                                             }}
+                            />
+                        ))}
+                    </ToggleGroup>
                 </FormGroup>
             </Form>
         </Modal>
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
index 9b8c0d74..f507b858 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
@@ -114,7 +114,7 @@ export const ProjectsPage = () => {
             <TableComposable aria-label="Projects" variant={"compact"}>
                 <Thead>
                     <Tr>
-                        <Th key='type'>Runtime</Th>
+                        <Th key='type' >Runtime</Th>
                         <Th key='projectId'>Project ID</Th>
                         <Th key='name'>Name</Th>
                         <Th key='description'>Description</Th>
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
index d2a7323a..053f0a2f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
@@ -9,7 +9,7 @@ import '../designer/karavan.css';
 import { Td, Tr} from "@patternfly/react-table";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
 import CopyIcon from "@patternfly/react-icons/dist/esm/icons/copy-icon";
-import {DeploymentStatus, Project} from '../api/ProjectModels';
+import {Project} from '../api/ProjectModels';
 import {
     useAppConfigStore,
     useLogStore,
@@ -17,8 +17,7 @@ import {
 } from "../api/ProjectStore";
 import {ProjectEventBus} from "../api/ProjectEventBus";
 import {shallow} from "zustand/shallow";
-import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
-import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
+import {CamelIcon, QuarkusIcon, SpringIcon} from "../designer/utils/KaravanIcons";
 
 interface Props {
     project: Project
@@ -45,15 +44,20 @@ export const ProjectsTableRow = (props: Props) => {
         });
     }
 
+    function getIcon(runtime: string) {
+        if (runtime === 'quarkus') return QuarkusIcon();
+        else if (runtime === 'spring-boot') return SpringIcon();
+        else if (runtime === 'camel-main') return CamelIcon();
+    }
+
     const project = props.project;
     const isBuildIn = ['kamelets', 'templates'].includes(project.projectId);
     const badge = isBuildIn ? project.projectId.toUpperCase().charAt(0) : project.runtime.substring(0, 1).toUpperCase();
+    const commit = project.lastCommit ? project.lastCommit?.substr(0, 7) : "...";
     return (
         <Tr key={project.projectId}>
-            <Td modifier={"fitContent"}>
-                <Tooltip content={project.runtime} position={"left"}>
-                    <Badge isRead={isBuildIn} className="runtime-badge">{badge}</Badge>
-                </Tooltip>
+            <Td className="icon-td">
+                {getIcon(project.runtime)}
             </Td>
             <Td>
                 <Button style={{padding: '6px'}} variant={"link"} onClick={e => {
@@ -68,7 +72,7 @@ export const ProjectsTableRow = (props: Props) => {
             <Td>{project.description}</Td>
             <Td isActionCell>
                 <Tooltip content={project.lastCommit} position={"bottom"}>
-                    <Badge className="badge">{project.lastCommit?.substr(0, 7)}</Badge>
+                    <Badge className="badge">{commit}</Badge>
                 </Tooltip>
             </Td>
             <Td noPadding style={{width: "180px"}}>
diff --git a/karavan-web/karavan-app/src/main/webui/tsconfig.json b/karavan-web/karavan-app/src/main/webui/tsconfig.json
index f8c6a251..d1ffa220 100644
--- a/karavan-web/karavan-app/src/main/webui/tsconfig.json
+++ b/karavan-web/karavan-app/src/main/webui/tsconfig.json
@@ -1,13 +1,15 @@
 {
   "compilerOptions": {
-    "target": "es5",
-    "module": "esnext",
+    "target": "ES2022",
+    "module": "ES2022",
     "outDir": "out",
     "sourceMap": true,
     "lib": [
       "dom",
       "dom.iterable",
-      "esnext"
+      "esnext",
+      "ES2021",
+      "ES2021.String"
     ],
     "allowJs": true,
     "skipLibCheck": true,
@@ -24,5 +26,9 @@
   },
   "include": [
     "src"
+  ],
+  "exclude": [
+    "node_modules",
+    "**/test/*"
   ]
 }