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 2024/03/01 16:36:40 UTC

(camel-karavan) branch main updated: Fix #1159

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 d0a18eaa Fix #1159
d0a18eaa is described below

commit d0a18eaa73553f412cbc0c1c6860b4b9f96c4929
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Fri Mar 1 11:36:33 2024 -0500

    Fix #1159
---
 .../src/main/webui/src/designer/DesignerStore.ts   |  2 +-
 .../webui/src/designer/route/RouteDesigner.tsx     |  2 +-
 .../src/designer/route/useRouteDesignerHook.tsx    |  2 +-
 .../webui/src/designer/selector/DslPreferences.tsx | 76 +++++++++++++++++
 .../webui/src/designer/selector}/DslSelector.css   | 20 +++++
 .../webui/src/designer/selector}/DslSelector.tsx   | 97 +++++++++++++++-------
 karavan-designer/src/designer/DesignerStore.ts     |  2 +-
 .../src/designer/route/RouteDesigner.tsx           |  2 +-
 .../src/designer/route/useRouteDesignerHook.tsx    |  2 +-
 .../src/designer/selector/DslPreferences.tsx       | 76 +++++++++++++++++
 .../src/designer/selector}/DslSelector.css         | 20 +++++
 .../src/designer/selector}/DslSelector.tsx         | 97 +++++++++++++++-------
 karavan-space/src/designer/DesignerStore.ts        |  2 +-
 karavan-space/src/designer/route/RouteDesigner.tsx |  2 +-
 .../src/designer/route/useRouteDesignerHook.tsx    |  2 +-
 .../src/designer/selector/DslPreferences.tsx       | 76 +++++++++++++++++
 .../src/designer/selector}/DslSelector.css         | 20 +++++
 .../src/designer/selector}/DslSelector.tsx         | 97 +++++++++++++++-------
 18 files changed, 504 insertions(+), 93 deletions(-)

diff --git a/karavan-app/src/main/webui/src/designer/DesignerStore.ts b/karavan-app/src/main/webui/src/designer/DesignerStore.ts
index e08d9a19..c19038ad 100644
--- a/karavan-app/src/main/webui/src/designer/DesignerStore.ts
+++ b/karavan-app/src/main/webui/src/designer/DesignerStore.ts
@@ -130,7 +130,7 @@ export const useSelectorStore = createWithEqualityFn<SelectorStateState>((set) =
     clearSelectedLabels: () => {
         set((state: SelectorStateState) => {
             state.selectedLabels.length = 0;
-            return state;
+            return {selectedLabels : [... state.selectedLabels]};
         })
     },
     setSelectedLabels: (selectedLabels: string []) => {
diff --git a/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
index 77678b3a..9ef5e827 100644
--- a/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
+++ b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
@@ -23,7 +23,7 @@ import {
     Button
 } from '@patternfly/react-core';
 import '../karavan.css';
-import {DslSelector} from "./DslSelector";
+import {DslSelector} from "../selector/DslSelector";
 import {DslProperties} from "../property/DslProperties";
 import {DslConnections} from "./DslConnections";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
diff --git a/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx b/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
index 543f2a00..0f9b7236 100644
--- a/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
@@ -226,7 +226,7 @@ export function useRouteDesignerHook () {
         setParentDsl(parentDsl);
         setShowSteps(showSteps);
         setSelectedPosition(position);
-        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
+        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'components' : 'eip');
     }
 
     function onDslSelect (dsl: DslMetaModel, parentId: string, position?: number | undefined) {
diff --git a/karavan-app/src/main/webui/src/designer/selector/DslPreferences.tsx b/karavan-app/src/main/webui/src/designer/selector/DslPreferences.tsx
new file mode 100644
index 00000000..87844b5a
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/selector/DslPreferences.tsx
@@ -0,0 +1,76 @@
+/*
+ * 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 {DslMetaModel} from "../utils/DslMetaModel";
+
+export class PreferredElements {
+    eip: PreferredElement[] = [];
+    components: PreferredElement[] = [];
+    kamelets: PreferredElement[] = [];
+
+    public constructor(init?: Partial<PreferredElements>) {
+        Object.assign(this, init);
+    }
+}
+
+export class PreferredElement {
+    dslKey: string = '';
+    count: number = 0;
+
+    public constructor(init?: Partial<PreferredElement>) {
+        Object.assign(this, init);
+    }
+}
+
+const PREFERRED_ELEMENTS_STORAGE_NAME = 'PREFERRED_ELEMENTS'
+
+function addCount(pe: PreferredElement): PreferredElement {
+    return new PreferredElement({dslKey: pe.dslKey, count: pe.count + 1});
+}
+
+export function getPreferredElements(type: 'eip' | 'components' | 'kamelets'): string[] {
+    const result: string[] = [];
+    try {
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        if (local !== null) {
+            const pes = new PreferredElements(JSON.parse(local));
+            (pes as any)[type].forEach((pe: PreferredElement) => result.push(pe.dslKey));
+        }
+    } catch (e) {
+        console.log(e);
+    }
+    return result;
+}
+
+export function addPreferredElement(type: 'eip' | 'components' | 'kamelets', dsl: DslMetaModel) {
+    try {
+        const dslKey = type === 'eip' ? dsl.dsl : (type === 'components' ? dsl.uri : dsl.name);
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        const pes = local !== null ? new PreferredElements(JSON.parse(local)) : new PreferredElements();
+        let list: PreferredElement[] = (pes as any)[type];
+        if (list.findIndex(pe => pe.dslKey === dslKey) !== -1) {
+            list = list.map(pe => pe.dslKey === dslKey ? addCount(pe) : pe);
+        } else {
+            list.push(new PreferredElement({dslKey: dslKey, count: 1}))
+        }
+        list = list.sort((a, b) => b.count - a.count).filter((_, i) => i < 20);
+        (pes as any)[type] = [...list];
+        localStorage.setItem(PREFERRED_ELEMENTS_STORAGE_NAME, JSON.stringify(pes));
+    } catch (e) {
+        console.log(e);
+    }
+}
diff --git a/karavan-designer/src/designer/route/DslSelector.css b/karavan-app/src/main/webui/src/designer/selector/DslSelector.css
similarity index 86%
copy from karavan-designer/src/designer/route/DslSelector.css
copy to karavan-app/src/main/webui/src/designer/selector/DslSelector.css
index 4bca7774..279c1f41 100644
--- a/karavan-designer/src/designer/route/DslSelector.css
+++ b/karavan-app/src/main/webui/src/designer/selector/DslSelector.css
@@ -79,6 +79,7 @@
     display: -webkit-box;
     -webkit-line-clamp: 2;
     -webkit-box-orient: vertical;
+    color: var(--pf-v5-global--Color--200);
 }
 .dsl-modal .pf-v5-c-card__footer {
     padding-bottom: 1em;
@@ -91,6 +92,11 @@
     flex-wrap: wrap;
 }
 
+.dsl-modal .dsl-card .dsl-card-title {
+    color: var(--pf-v5-global--Color--100);
+    font-weight: bold;
+}
+
 .dsl-modal .dsl-card .header-labels {
     padding: 5px;
     display: flex;
@@ -120,3 +126,17 @@
 .dsl-modal .dsl-card:hover .labels {
     opacity: 1;
 }
+
+.dsl-modal .dsl-card:hover p {
+    color: var(--pf-v5-global--Color--100);
+}
+
+.dsl-modal .dsl-fast-card .header p {
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+.dsl-modal .dsl-fast-card .header {
+    padding: 10px;
+}
diff --git a/karavan-designer/src/designer/route/DslSelector.tsx b/karavan-app/src/main/webui/src/designer/selector/DslSelector.tsx
similarity index 71%
copy from karavan-designer/src/designer/route/DslSelector.tsx
copy to karavan-app/src/main/webui/src/designer/selector/DslSelector.tsx
index c7dde3e7..fac2a6c6 100644
--- a/karavan-designer/src/designer/route/DslSelector.tsx
+++ b/karavan-app/src/main/webui/src/designer/selector/DslSelector.tsx
@@ -17,38 +17,37 @@
 import React, {useEffect, useState} from 'react';
 import {
     Badge,
+    Button,
     Card,
     CardBody,
     CardFooter,
     CardHeader,
     Flex,
     FlexItem,
-    Form,
-    FormGroup,
     Gallery,
     Modal,
     PageSection,
+    Switch,
     Tab,
     Tabs,
     TabTitleText,
     Text,
-    ToggleGroup,
-    ToggleGroupItem,
-    Switch,
     TextInputGroup,
     TextInputGroupMain,
     TextInputGroupUtilities,
-    Button
+    ToggleGroup,
+    ToggleGroupItem
 } from '@patternfly/react-core';
 import './DslSelector.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {useDesignerStore, useSelectorStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
-import {useRouteDesignerHook} from "./useRouteDesignerHook";
-import { ComponentApi } from 'karavan-core/lib/api/ComponentApi';
-import { KameletApi } from 'karavan-core/lib/api/KameletApi';
+import {useRouteDesignerHook} from "../route/useRouteDesignerHook";
+import {ComponentApi} from 'karavan-core/lib/api/ComponentApi';
+import {KameletApi} from 'karavan-core/lib/api/KameletApi';
 import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon";
+import {addPreferredElement, getPreferredElements} from "./DslPreferences";
 
 interface Props {
     tabIndex?: string | number
@@ -57,20 +56,25 @@ interface Props {
 export function DslSelector (props: Props) {
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex,
-        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel] =
+        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel, clearSelectedLabels] =
         useSelectorStore((s) =>
             [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
-                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
+                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel, s.clearSelectedLabels], shallow)
 
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
     const {onDslSelect} = useRouteDesignerHook();
 
-
     const [filter, setFilter] = useState<string>('');
     const [customOnly, setCustomOnly] = useState<boolean>(false);
+    const [preferredEip, setPreferredEip] = useState<string[]>([]);
+    const [preferredComponents, setPreferredComponents] = useState<string[]>([]);
+    const [preferredKamelets, setPreferredKamelets] = useState<string[]>([]);
 
     useEffect(() => {
+        setPreferredEip(getPreferredElements('eip'));
+        setPreferredComponents(getPreferredElements('components'));
+        setPreferredKamelets(getPreferredElements('kamelets'));
     }, [selectedLabels]);
 
 
@@ -83,12 +87,14 @@ export function DslSelector (props: Props) {
         setFilter('');
         setShowSelector(false);
         onDslSelect(dsl, parentId, selectedPosition);
+        const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 'components' : 'kamelets');
+        addPreferredElement(type, dsl)
     }
 
     function searchInput() {
         return (
             <Flex className="search">
-                {selectorTabIndex === 'kamelet' && <FlexItem>
+                {selectorTabIndex === 'kamelets' && <FlexItem>
                     <Switch
                         label="Custom only"
                         id="switch"
@@ -127,10 +133,11 @@ export function DslSelector (props: Props) {
                 </CardHeader>
                 <CardHeader>
                     {CamelUi.getIconForDsl(dsl)}
-                    <Text>{dsl.title}</Text>
+                    <Text className='dsl-card-title'>{dsl.title}</Text>
                 </CardHeader>
                 <CardBody>
-                    <Text>{dsl.description}</Text>
+                    {/*<Text>{dsl.description}</Text>*/}
+                    <Text className="pf-v5-u-color-200">{dsl.description}</Text>
                 </CardBody>
                 <CardFooter className="footer-labels">
                     <div style={{display: "flex", flexDirection: "row", justifyContent: "start"}}>
@@ -143,6 +150,18 @@ export function DslSelector (props: Props) {
         )
     }
 
+    function getFastCard(dsl: DslMetaModel, index: number) {
+        return (
+            <Card key={dsl.dsl + index} isCompact className="dsl-card dsl-fast-card"
+                  onClick={event => selectDsl(event, dsl)}>
+                <CardHeader className='header'>
+                    {CamelUi.getIconForDsl(dsl)}
+                    <Text className='dsl-fast-card-title'>{dsl.title}</Text>
+                </CardHeader>
+            </Card>
+        )
+    }
+
     function close() {
         setFilter('');
         setShowSelector(false);
@@ -156,10 +175,10 @@ export function DslSelector (props: Props) {
         }
     }
 
-    function filterElements(elements: DslMetaModel[]):DslMetaModel[] {
+    function filterElements(elements: DslMetaModel[], type: 'eip' | 'components' | 'kamelets'):DslMetaModel[] {
         return elements.filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, filter))
             .filter((dsl: DslMetaModel) => {
-                if (!isEip || selectedLabels.length === 0) {
+                if (type !== 'eip' || selectedLabels.length === 0) {
                     return true;
                 } else {
                     return dsl.labels.split(",").some(r => selectedLabels.includes(r));
@@ -181,16 +200,24 @@ export function DslSelector (props: Props) {
         .filter(dsl => (!blockedKamelets.includes(dsl.name)));
     if (customOnly) kameletElements = kameletElements.filter(k => KameletApi.getCustomKameletNames().includes(k.name));
 
-    const filteredEipElements = filterElements(eipElements);
-    const filteredComponentElements = filterElements(componentElements);
-    const filteredKameletElements = filterElements(kameletElements);
+    const elements = navigation === 'components'
+        ? componentElements
+        : (navigation === 'kamelets' ? kameletElements : eipElements);
+
+    const preferredElements = navigation === 'components'
+        ? preferredComponents
+        : (navigation === 'kamelets' ? preferredKamelets : preferredEip);
+
+    const filteredEipElements = filterElements(eipElements, 'eip');
+    const filteredComponentElements = filterElements(componentElements, 'components');
+    const filteredKameletElements = filterElements(kameletElements, 'kamelets');
 
     const eipLabels = [...new Set(eipElements.map(e => e.labels).join(",").split(",").filter(e => e !== 'eip'))];
 
 
-    const filteredElement = navigation === 'component'
+    const filteredElements = navigation === 'components'
         ? filteredComponentElements
-        : (navigation === 'kamelet' ? filteredKameletElements : filteredEipElements);
+        : (navigation === 'kamelets' ? filteredKameletElements : filteredEipElements);
 
     console.log(parentDsl)
 
@@ -216,15 +243,15 @@ export function DslSelector (props: Props) {
                                 </Tab>
                             }
                             {!isRouteConfig &&
-                                <Tab eventKey={'kamelet'} key={"tab-kamelet"}
+                                <Tab eventKey={'components'} key={'tab-component'}
                                      title={
-                                         <TabTitleText>{`Kamelets (${filteredKameletElements?.length})`}</TabTitleText>}>
+                                         <TabTitleText>{`Components (${filteredComponentElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                             {!isRouteConfig &&
-                                <Tab eventKey={'component'} key={'tab-component'}
+                                <Tab eventKey={'kamelets'} key={"tab-kamelet"}
                                      title={
-                                         <TabTitleText>{`Components (${filteredComponentElements?.length})`}</TabTitleText>}>
+                                         <TabTitleText>{`Kamelets (${filteredKameletElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                         </Tabs>
@@ -241,9 +268,23 @@ export function DslSelector (props: Props) {
                         isSelected={selectedLabels.includes(eipLabel)}
                         onChange={selected => selectLabel(eipLabel)}
                     />)}
+                    <ToggleGroupItem key='clean' buttonId='clean' isSelected={false} onChange={clearSelectedLabels} icon={<TimesIcon/>}/>
                 </ToggleGroup>}
-                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery">
-                    {showSelector && filteredElement.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
+                <Gallery key={"fast-gallery-" + navigation} hasGutter className="dsl-gallery" minWidths={{default: '150px'}}>
+                    {showSelector && elements
+                        .filter((d: DslMetaModel) => {
+                            if (isEip) {
+                                return preferredElements.includes(d.dsl);
+                            } else if (navigation === 'components') {
+                                return d.uri && preferredElements.includes(d.uri)
+                            } else {
+                                return preferredElements.includes(d.name)
+                            }
+                        })
+                        .filter((_, i) => i < 7).map((dsl: DslMetaModel, index: number) => getFastCard(dsl, index))}
+                </Gallery>
+                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery" minWidths={{default: '200px'}}>
+                    {showSelector && filteredElements.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
                 </Gallery>
             </PageSection>
         </Modal>
diff --git a/karavan-designer/src/designer/DesignerStore.ts b/karavan-designer/src/designer/DesignerStore.ts
index e08d9a19..c19038ad 100644
--- a/karavan-designer/src/designer/DesignerStore.ts
+++ b/karavan-designer/src/designer/DesignerStore.ts
@@ -130,7 +130,7 @@ export const useSelectorStore = createWithEqualityFn<SelectorStateState>((set) =
     clearSelectedLabels: () => {
         set((state: SelectorStateState) => {
             state.selectedLabels.length = 0;
-            return state;
+            return {selectedLabels : [... state.selectedLabels]};
         })
     },
     setSelectedLabels: (selectedLabels: string []) => {
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index 77678b3a..9ef5e827 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -23,7 +23,7 @@ import {
     Button
 } from '@patternfly/react-core';
 import '../karavan.css';
-import {DslSelector} from "./DslSelector";
+import {DslSelector} from "../selector/DslSelector";
 import {DslProperties} from "../property/DslProperties";
 import {DslConnections} from "./DslConnections";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index 543f2a00..0f9b7236 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -226,7 +226,7 @@ export function useRouteDesignerHook () {
         setParentDsl(parentDsl);
         setShowSteps(showSteps);
         setSelectedPosition(position);
-        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
+        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'components' : 'eip');
     }
 
     function onDslSelect (dsl: DslMetaModel, parentId: string, position?: number | undefined) {
diff --git a/karavan-designer/src/designer/selector/DslPreferences.tsx b/karavan-designer/src/designer/selector/DslPreferences.tsx
new file mode 100644
index 00000000..87844b5a
--- /dev/null
+++ b/karavan-designer/src/designer/selector/DslPreferences.tsx
@@ -0,0 +1,76 @@
+/*
+ * 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 {DslMetaModel} from "../utils/DslMetaModel";
+
+export class PreferredElements {
+    eip: PreferredElement[] = [];
+    components: PreferredElement[] = [];
+    kamelets: PreferredElement[] = [];
+
+    public constructor(init?: Partial<PreferredElements>) {
+        Object.assign(this, init);
+    }
+}
+
+export class PreferredElement {
+    dslKey: string = '';
+    count: number = 0;
+
+    public constructor(init?: Partial<PreferredElement>) {
+        Object.assign(this, init);
+    }
+}
+
+const PREFERRED_ELEMENTS_STORAGE_NAME = 'PREFERRED_ELEMENTS'
+
+function addCount(pe: PreferredElement): PreferredElement {
+    return new PreferredElement({dslKey: pe.dslKey, count: pe.count + 1});
+}
+
+export function getPreferredElements(type: 'eip' | 'components' | 'kamelets'): string[] {
+    const result: string[] = [];
+    try {
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        if (local !== null) {
+            const pes = new PreferredElements(JSON.parse(local));
+            (pes as any)[type].forEach((pe: PreferredElement) => result.push(pe.dslKey));
+        }
+    } catch (e) {
+        console.log(e);
+    }
+    return result;
+}
+
+export function addPreferredElement(type: 'eip' | 'components' | 'kamelets', dsl: DslMetaModel) {
+    try {
+        const dslKey = type === 'eip' ? dsl.dsl : (type === 'components' ? dsl.uri : dsl.name);
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        const pes = local !== null ? new PreferredElements(JSON.parse(local)) : new PreferredElements();
+        let list: PreferredElement[] = (pes as any)[type];
+        if (list.findIndex(pe => pe.dslKey === dslKey) !== -1) {
+            list = list.map(pe => pe.dslKey === dslKey ? addCount(pe) : pe);
+        } else {
+            list.push(new PreferredElement({dslKey: dslKey, count: 1}))
+        }
+        list = list.sort((a, b) => b.count - a.count).filter((_, i) => i < 20);
+        (pes as any)[type] = [...list];
+        localStorage.setItem(PREFERRED_ELEMENTS_STORAGE_NAME, JSON.stringify(pes));
+    } catch (e) {
+        console.log(e);
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/route/DslSelector.css b/karavan-designer/src/designer/selector/DslSelector.css
similarity index 86%
rename from karavan-app/src/main/webui/src/designer/route/DslSelector.css
rename to karavan-designer/src/designer/selector/DslSelector.css
index 4bca7774..279c1f41 100644
--- a/karavan-app/src/main/webui/src/designer/route/DslSelector.css
+++ b/karavan-designer/src/designer/selector/DslSelector.css
@@ -79,6 +79,7 @@
     display: -webkit-box;
     -webkit-line-clamp: 2;
     -webkit-box-orient: vertical;
+    color: var(--pf-v5-global--Color--200);
 }
 .dsl-modal .pf-v5-c-card__footer {
     padding-bottom: 1em;
@@ -91,6 +92,11 @@
     flex-wrap: wrap;
 }
 
+.dsl-modal .dsl-card .dsl-card-title {
+    color: var(--pf-v5-global--Color--100);
+    font-weight: bold;
+}
+
 .dsl-modal .dsl-card .header-labels {
     padding: 5px;
     display: flex;
@@ -120,3 +126,17 @@
 .dsl-modal .dsl-card:hover .labels {
     opacity: 1;
 }
+
+.dsl-modal .dsl-card:hover p {
+    color: var(--pf-v5-global--Color--100);
+}
+
+.dsl-modal .dsl-fast-card .header p {
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+.dsl-modal .dsl-fast-card .header {
+    padding: 10px;
+}
diff --git a/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx b/karavan-designer/src/designer/selector/DslSelector.tsx
similarity index 71%
rename from karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
rename to karavan-designer/src/designer/selector/DslSelector.tsx
index c7dde3e7..fac2a6c6 100644
--- a/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
+++ b/karavan-designer/src/designer/selector/DslSelector.tsx
@@ -17,38 +17,37 @@
 import React, {useEffect, useState} from 'react';
 import {
     Badge,
+    Button,
     Card,
     CardBody,
     CardFooter,
     CardHeader,
     Flex,
     FlexItem,
-    Form,
-    FormGroup,
     Gallery,
     Modal,
     PageSection,
+    Switch,
     Tab,
     Tabs,
     TabTitleText,
     Text,
-    ToggleGroup,
-    ToggleGroupItem,
-    Switch,
     TextInputGroup,
     TextInputGroupMain,
     TextInputGroupUtilities,
-    Button
+    ToggleGroup,
+    ToggleGroupItem
 } from '@patternfly/react-core';
 import './DslSelector.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {useDesignerStore, useSelectorStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
-import {useRouteDesignerHook} from "./useRouteDesignerHook";
-import { ComponentApi } from 'karavan-core/lib/api/ComponentApi';
-import { KameletApi } from 'karavan-core/lib/api/KameletApi';
+import {useRouteDesignerHook} from "../route/useRouteDesignerHook";
+import {ComponentApi} from 'karavan-core/lib/api/ComponentApi';
+import {KameletApi} from 'karavan-core/lib/api/KameletApi';
 import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon";
+import {addPreferredElement, getPreferredElements} from "./DslPreferences";
 
 interface Props {
     tabIndex?: string | number
@@ -57,20 +56,25 @@ interface Props {
 export function DslSelector (props: Props) {
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex,
-        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel] =
+        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel, clearSelectedLabels] =
         useSelectorStore((s) =>
             [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
-                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
+                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel, s.clearSelectedLabels], shallow)
 
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
     const {onDslSelect} = useRouteDesignerHook();
 
-
     const [filter, setFilter] = useState<string>('');
     const [customOnly, setCustomOnly] = useState<boolean>(false);
+    const [preferredEip, setPreferredEip] = useState<string[]>([]);
+    const [preferredComponents, setPreferredComponents] = useState<string[]>([]);
+    const [preferredKamelets, setPreferredKamelets] = useState<string[]>([]);
 
     useEffect(() => {
+        setPreferredEip(getPreferredElements('eip'));
+        setPreferredComponents(getPreferredElements('components'));
+        setPreferredKamelets(getPreferredElements('kamelets'));
     }, [selectedLabels]);
 
 
@@ -83,12 +87,14 @@ export function DslSelector (props: Props) {
         setFilter('');
         setShowSelector(false);
         onDslSelect(dsl, parentId, selectedPosition);
+        const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 'components' : 'kamelets');
+        addPreferredElement(type, dsl)
     }
 
     function searchInput() {
         return (
             <Flex className="search">
-                {selectorTabIndex === 'kamelet' && <FlexItem>
+                {selectorTabIndex === 'kamelets' && <FlexItem>
                     <Switch
                         label="Custom only"
                         id="switch"
@@ -127,10 +133,11 @@ export function DslSelector (props: Props) {
                 </CardHeader>
                 <CardHeader>
                     {CamelUi.getIconForDsl(dsl)}
-                    <Text>{dsl.title}</Text>
+                    <Text className='dsl-card-title'>{dsl.title}</Text>
                 </CardHeader>
                 <CardBody>
-                    <Text>{dsl.description}</Text>
+                    {/*<Text>{dsl.description}</Text>*/}
+                    <Text className="pf-v5-u-color-200">{dsl.description}</Text>
                 </CardBody>
                 <CardFooter className="footer-labels">
                     <div style={{display: "flex", flexDirection: "row", justifyContent: "start"}}>
@@ -143,6 +150,18 @@ export function DslSelector (props: Props) {
         )
     }
 
+    function getFastCard(dsl: DslMetaModel, index: number) {
+        return (
+            <Card key={dsl.dsl + index} isCompact className="dsl-card dsl-fast-card"
+                  onClick={event => selectDsl(event, dsl)}>
+                <CardHeader className='header'>
+                    {CamelUi.getIconForDsl(dsl)}
+                    <Text className='dsl-fast-card-title'>{dsl.title}</Text>
+                </CardHeader>
+            </Card>
+        )
+    }
+
     function close() {
         setFilter('');
         setShowSelector(false);
@@ -156,10 +175,10 @@ export function DslSelector (props: Props) {
         }
     }
 
-    function filterElements(elements: DslMetaModel[]):DslMetaModel[] {
+    function filterElements(elements: DslMetaModel[], type: 'eip' | 'components' | 'kamelets'):DslMetaModel[] {
         return elements.filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, filter))
             .filter((dsl: DslMetaModel) => {
-                if (!isEip || selectedLabels.length === 0) {
+                if (type !== 'eip' || selectedLabels.length === 0) {
                     return true;
                 } else {
                     return dsl.labels.split(",").some(r => selectedLabels.includes(r));
@@ -181,16 +200,24 @@ export function DslSelector (props: Props) {
         .filter(dsl => (!blockedKamelets.includes(dsl.name)));
     if (customOnly) kameletElements = kameletElements.filter(k => KameletApi.getCustomKameletNames().includes(k.name));
 
-    const filteredEipElements = filterElements(eipElements);
-    const filteredComponentElements = filterElements(componentElements);
-    const filteredKameletElements = filterElements(kameletElements);
+    const elements = navigation === 'components'
+        ? componentElements
+        : (navigation === 'kamelets' ? kameletElements : eipElements);
+
+    const preferredElements = navigation === 'components'
+        ? preferredComponents
+        : (navigation === 'kamelets' ? preferredKamelets : preferredEip);
+
+    const filteredEipElements = filterElements(eipElements, 'eip');
+    const filteredComponentElements = filterElements(componentElements, 'components');
+    const filteredKameletElements = filterElements(kameletElements, 'kamelets');
 
     const eipLabels = [...new Set(eipElements.map(e => e.labels).join(",").split(",").filter(e => e !== 'eip'))];
 
 
-    const filteredElement = navigation === 'component'
+    const filteredElements = navigation === 'components'
         ? filteredComponentElements
-        : (navigation === 'kamelet' ? filteredKameletElements : filteredEipElements);
+        : (navigation === 'kamelets' ? filteredKameletElements : filteredEipElements);
 
     console.log(parentDsl)
 
@@ -216,15 +243,15 @@ export function DslSelector (props: Props) {
                                 </Tab>
                             }
                             {!isRouteConfig &&
-                                <Tab eventKey={'kamelet'} key={"tab-kamelet"}
+                                <Tab eventKey={'components'} key={'tab-component'}
                                      title={
-                                         <TabTitleText>{`Kamelets (${filteredKameletElements?.length})`}</TabTitleText>}>
+                                         <TabTitleText>{`Components (${filteredComponentElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                             {!isRouteConfig &&
-                                <Tab eventKey={'component'} key={'tab-component'}
+                                <Tab eventKey={'kamelets'} key={"tab-kamelet"}
                                      title={
-                                         <TabTitleText>{`Components (${filteredComponentElements?.length})`}</TabTitleText>}>
+                                         <TabTitleText>{`Kamelets (${filteredKameletElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                         </Tabs>
@@ -241,9 +268,23 @@ export function DslSelector (props: Props) {
                         isSelected={selectedLabels.includes(eipLabel)}
                         onChange={selected => selectLabel(eipLabel)}
                     />)}
+                    <ToggleGroupItem key='clean' buttonId='clean' isSelected={false} onChange={clearSelectedLabels} icon={<TimesIcon/>}/>
                 </ToggleGroup>}
-                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery">
-                    {showSelector && filteredElement.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
+                <Gallery key={"fast-gallery-" + navigation} hasGutter className="dsl-gallery" minWidths={{default: '150px'}}>
+                    {showSelector && elements
+                        .filter((d: DslMetaModel) => {
+                            if (isEip) {
+                                return preferredElements.includes(d.dsl);
+                            } else if (navigation === 'components') {
+                                return d.uri && preferredElements.includes(d.uri)
+                            } else {
+                                return preferredElements.includes(d.name)
+                            }
+                        })
+                        .filter((_, i) => i < 7).map((dsl: DslMetaModel, index: number) => getFastCard(dsl, index))}
+                </Gallery>
+                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery" minWidths={{default: '200px'}}>
+                    {showSelector && filteredElements.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
                 </Gallery>
             </PageSection>
         </Modal>
diff --git a/karavan-space/src/designer/DesignerStore.ts b/karavan-space/src/designer/DesignerStore.ts
index e08d9a19..c19038ad 100644
--- a/karavan-space/src/designer/DesignerStore.ts
+++ b/karavan-space/src/designer/DesignerStore.ts
@@ -130,7 +130,7 @@ export const useSelectorStore = createWithEqualityFn<SelectorStateState>((set) =
     clearSelectedLabels: () => {
         set((state: SelectorStateState) => {
             state.selectedLabels.length = 0;
-            return state;
+            return {selectedLabels : [... state.selectedLabels]};
         })
     },
     setSelectedLabels: (selectedLabels: string []) => {
diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx b/karavan-space/src/designer/route/RouteDesigner.tsx
index 77678b3a..9ef5e827 100644
--- a/karavan-space/src/designer/route/RouteDesigner.tsx
+++ b/karavan-space/src/designer/route/RouteDesigner.tsx
@@ -23,7 +23,7 @@ import {
     Button
 } from '@patternfly/react-core';
 import '../karavan.css';
-import {DslSelector} from "./DslSelector";
+import {DslSelector} from "../selector/DslSelector";
 import {DslProperties} from "../property/DslProperties";
 import {DslConnections} from "./DslConnections";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
diff --git a/karavan-space/src/designer/route/useRouteDesignerHook.tsx b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
index 543f2a00..0f9b7236 100644
--- a/karavan-space/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
@@ -226,7 +226,7 @@ export function useRouteDesignerHook () {
         setParentDsl(parentDsl);
         setShowSteps(showSteps);
         setSelectedPosition(position);
-        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
+        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'components' : 'eip');
     }
 
     function onDslSelect (dsl: DslMetaModel, parentId: string, position?: number | undefined) {
diff --git a/karavan-space/src/designer/selector/DslPreferences.tsx b/karavan-space/src/designer/selector/DslPreferences.tsx
new file mode 100644
index 00000000..87844b5a
--- /dev/null
+++ b/karavan-space/src/designer/selector/DslPreferences.tsx
@@ -0,0 +1,76 @@
+/*
+ * 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 {DslMetaModel} from "../utils/DslMetaModel";
+
+export class PreferredElements {
+    eip: PreferredElement[] = [];
+    components: PreferredElement[] = [];
+    kamelets: PreferredElement[] = [];
+
+    public constructor(init?: Partial<PreferredElements>) {
+        Object.assign(this, init);
+    }
+}
+
+export class PreferredElement {
+    dslKey: string = '';
+    count: number = 0;
+
+    public constructor(init?: Partial<PreferredElement>) {
+        Object.assign(this, init);
+    }
+}
+
+const PREFERRED_ELEMENTS_STORAGE_NAME = 'PREFERRED_ELEMENTS'
+
+function addCount(pe: PreferredElement): PreferredElement {
+    return new PreferredElement({dslKey: pe.dslKey, count: pe.count + 1});
+}
+
+export function getPreferredElements(type: 'eip' | 'components' | 'kamelets'): string[] {
+    const result: string[] = [];
+    try {
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        if (local !== null) {
+            const pes = new PreferredElements(JSON.parse(local));
+            (pes as any)[type].forEach((pe: PreferredElement) => result.push(pe.dslKey));
+        }
+    } catch (e) {
+        console.log(e);
+    }
+    return result;
+}
+
+export function addPreferredElement(type: 'eip' | 'components' | 'kamelets', dsl: DslMetaModel) {
+    try {
+        const dslKey = type === 'eip' ? dsl.dsl : (type === 'components' ? dsl.uri : dsl.name);
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        const pes = local !== null ? new PreferredElements(JSON.parse(local)) : new PreferredElements();
+        let list: PreferredElement[] = (pes as any)[type];
+        if (list.findIndex(pe => pe.dslKey === dslKey) !== -1) {
+            list = list.map(pe => pe.dslKey === dslKey ? addCount(pe) : pe);
+        } else {
+            list.push(new PreferredElement({dslKey: dslKey, count: 1}))
+        }
+        list = list.sort((a, b) => b.count - a.count).filter((_, i) => i < 20);
+        (pes as any)[type] = [...list];
+        localStorage.setItem(PREFERRED_ELEMENTS_STORAGE_NAME, JSON.stringify(pes));
+    } catch (e) {
+        console.log(e);
+    }
+}
diff --git a/karavan-designer/src/designer/route/DslSelector.css b/karavan-space/src/designer/selector/DslSelector.css
similarity index 86%
rename from karavan-designer/src/designer/route/DslSelector.css
rename to karavan-space/src/designer/selector/DslSelector.css
index 4bca7774..279c1f41 100644
--- a/karavan-designer/src/designer/route/DslSelector.css
+++ b/karavan-space/src/designer/selector/DslSelector.css
@@ -79,6 +79,7 @@
     display: -webkit-box;
     -webkit-line-clamp: 2;
     -webkit-box-orient: vertical;
+    color: var(--pf-v5-global--Color--200);
 }
 .dsl-modal .pf-v5-c-card__footer {
     padding-bottom: 1em;
@@ -91,6 +92,11 @@
     flex-wrap: wrap;
 }
 
+.dsl-modal .dsl-card .dsl-card-title {
+    color: var(--pf-v5-global--Color--100);
+    font-weight: bold;
+}
+
 .dsl-modal .dsl-card .header-labels {
     padding: 5px;
     display: flex;
@@ -120,3 +126,17 @@
 .dsl-modal .dsl-card:hover .labels {
     opacity: 1;
 }
+
+.dsl-modal .dsl-card:hover p {
+    color: var(--pf-v5-global--Color--100);
+}
+
+.dsl-modal .dsl-fast-card .header p {
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+.dsl-modal .dsl-fast-card .header {
+    padding: 10px;
+}
diff --git a/karavan-designer/src/designer/route/DslSelector.tsx b/karavan-space/src/designer/selector/DslSelector.tsx
similarity index 71%
rename from karavan-designer/src/designer/route/DslSelector.tsx
rename to karavan-space/src/designer/selector/DslSelector.tsx
index c7dde3e7..fac2a6c6 100644
--- a/karavan-designer/src/designer/route/DslSelector.tsx
+++ b/karavan-space/src/designer/selector/DslSelector.tsx
@@ -17,38 +17,37 @@
 import React, {useEffect, useState} from 'react';
 import {
     Badge,
+    Button,
     Card,
     CardBody,
     CardFooter,
     CardHeader,
     Flex,
     FlexItem,
-    Form,
-    FormGroup,
     Gallery,
     Modal,
     PageSection,
+    Switch,
     Tab,
     Tabs,
     TabTitleText,
     Text,
-    ToggleGroup,
-    ToggleGroupItem,
-    Switch,
     TextInputGroup,
     TextInputGroupMain,
     TextInputGroupUtilities,
-    Button
+    ToggleGroup,
+    ToggleGroupItem
 } from '@patternfly/react-core';
 import './DslSelector.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {useDesignerStore, useSelectorStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
-import {useRouteDesignerHook} from "./useRouteDesignerHook";
-import { ComponentApi } from 'karavan-core/lib/api/ComponentApi';
-import { KameletApi } from 'karavan-core/lib/api/KameletApi';
+import {useRouteDesignerHook} from "../route/useRouteDesignerHook";
+import {ComponentApi} from 'karavan-core/lib/api/ComponentApi';
+import {KameletApi} from 'karavan-core/lib/api/KameletApi';
 import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon";
+import {addPreferredElement, getPreferredElements} from "./DslPreferences";
 
 interface Props {
     tabIndex?: string | number
@@ -57,20 +56,25 @@ interface Props {
 export function DslSelector (props: Props) {
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex,
-        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel] =
+        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel, clearSelectedLabels] =
         useSelectorStore((s) =>
             [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
-                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
+                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel, s.clearSelectedLabels], shallow)
 
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
     const {onDslSelect} = useRouteDesignerHook();
 
-
     const [filter, setFilter] = useState<string>('');
     const [customOnly, setCustomOnly] = useState<boolean>(false);
+    const [preferredEip, setPreferredEip] = useState<string[]>([]);
+    const [preferredComponents, setPreferredComponents] = useState<string[]>([]);
+    const [preferredKamelets, setPreferredKamelets] = useState<string[]>([]);
 
     useEffect(() => {
+        setPreferredEip(getPreferredElements('eip'));
+        setPreferredComponents(getPreferredElements('components'));
+        setPreferredKamelets(getPreferredElements('kamelets'));
     }, [selectedLabels]);
 
 
@@ -83,12 +87,14 @@ export function DslSelector (props: Props) {
         setFilter('');
         setShowSelector(false);
         onDslSelect(dsl, parentId, selectedPosition);
+        const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 'components' : 'kamelets');
+        addPreferredElement(type, dsl)
     }
 
     function searchInput() {
         return (
             <Flex className="search">
-                {selectorTabIndex === 'kamelet' && <FlexItem>
+                {selectorTabIndex === 'kamelets' && <FlexItem>
                     <Switch
                         label="Custom only"
                         id="switch"
@@ -127,10 +133,11 @@ export function DslSelector (props: Props) {
                 </CardHeader>
                 <CardHeader>
                     {CamelUi.getIconForDsl(dsl)}
-                    <Text>{dsl.title}</Text>
+                    <Text className='dsl-card-title'>{dsl.title}</Text>
                 </CardHeader>
                 <CardBody>
-                    <Text>{dsl.description}</Text>
+                    {/*<Text>{dsl.description}</Text>*/}
+                    <Text className="pf-v5-u-color-200">{dsl.description}</Text>
                 </CardBody>
                 <CardFooter className="footer-labels">
                     <div style={{display: "flex", flexDirection: "row", justifyContent: "start"}}>
@@ -143,6 +150,18 @@ export function DslSelector (props: Props) {
         )
     }
 
+    function getFastCard(dsl: DslMetaModel, index: number) {
+        return (
+            <Card key={dsl.dsl + index} isCompact className="dsl-card dsl-fast-card"
+                  onClick={event => selectDsl(event, dsl)}>
+                <CardHeader className='header'>
+                    {CamelUi.getIconForDsl(dsl)}
+                    <Text className='dsl-fast-card-title'>{dsl.title}</Text>
+                </CardHeader>
+            </Card>
+        )
+    }
+
     function close() {
         setFilter('');
         setShowSelector(false);
@@ -156,10 +175,10 @@ export function DslSelector (props: Props) {
         }
     }
 
-    function filterElements(elements: DslMetaModel[]):DslMetaModel[] {
+    function filterElements(elements: DslMetaModel[], type: 'eip' | 'components' | 'kamelets'):DslMetaModel[] {
         return elements.filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, filter))
             .filter((dsl: DslMetaModel) => {
-                if (!isEip || selectedLabels.length === 0) {
+                if (type !== 'eip' || selectedLabels.length === 0) {
                     return true;
                 } else {
                     return dsl.labels.split(",").some(r => selectedLabels.includes(r));
@@ -181,16 +200,24 @@ export function DslSelector (props: Props) {
         .filter(dsl => (!blockedKamelets.includes(dsl.name)));
     if (customOnly) kameletElements = kameletElements.filter(k => KameletApi.getCustomKameletNames().includes(k.name));
 
-    const filteredEipElements = filterElements(eipElements);
-    const filteredComponentElements = filterElements(componentElements);
-    const filteredKameletElements = filterElements(kameletElements);
+    const elements = navigation === 'components'
+        ? componentElements
+        : (navigation === 'kamelets' ? kameletElements : eipElements);
+
+    const preferredElements = navigation === 'components'
+        ? preferredComponents
+        : (navigation === 'kamelets' ? preferredKamelets : preferredEip);
+
+    const filteredEipElements = filterElements(eipElements, 'eip');
+    const filteredComponentElements = filterElements(componentElements, 'components');
+    const filteredKameletElements = filterElements(kameletElements, 'kamelets');
 
     const eipLabels = [...new Set(eipElements.map(e => e.labels).join(",").split(",").filter(e => e !== 'eip'))];
 
 
-    const filteredElement = navigation === 'component'
+    const filteredElements = navigation === 'components'
         ? filteredComponentElements
-        : (navigation === 'kamelet' ? filteredKameletElements : filteredEipElements);
+        : (navigation === 'kamelets' ? filteredKameletElements : filteredEipElements);
 
     console.log(parentDsl)
 
@@ -216,15 +243,15 @@ export function DslSelector (props: Props) {
                                 </Tab>
                             }
                             {!isRouteConfig &&
-                                <Tab eventKey={'kamelet'} key={"tab-kamelet"}
+                                <Tab eventKey={'components'} key={'tab-component'}
                                      title={
-                                         <TabTitleText>{`Kamelets (${filteredKameletElements?.length})`}</TabTitleText>}>
+                                         <TabTitleText>{`Components (${filteredComponentElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                             {!isRouteConfig &&
-                                <Tab eventKey={'component'} key={'tab-component'}
+                                <Tab eventKey={'kamelets'} key={"tab-kamelet"}
                                      title={
-                                         <TabTitleText>{`Components (${filteredComponentElements?.length})`}</TabTitleText>}>
+                                         <TabTitleText>{`Kamelets (${filteredKameletElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                         </Tabs>
@@ -241,9 +268,23 @@ export function DslSelector (props: Props) {
                         isSelected={selectedLabels.includes(eipLabel)}
                         onChange={selected => selectLabel(eipLabel)}
                     />)}
+                    <ToggleGroupItem key='clean' buttonId='clean' isSelected={false} onChange={clearSelectedLabels} icon={<TimesIcon/>}/>
                 </ToggleGroup>}
-                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery">
-                    {showSelector && filteredElement.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
+                <Gallery key={"fast-gallery-" + navigation} hasGutter className="dsl-gallery" minWidths={{default: '150px'}}>
+                    {showSelector && elements
+                        .filter((d: DslMetaModel) => {
+                            if (isEip) {
+                                return preferredElements.includes(d.dsl);
+                            } else if (navigation === 'components') {
+                                return d.uri && preferredElements.includes(d.uri)
+                            } else {
+                                return preferredElements.includes(d.name)
+                            }
+                        })
+                        .filter((_, i) => i < 7).map((dsl: DslMetaModel, index: number) => getFastCard(dsl, index))}
+                </Gallery>
+                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery" minWidths={{default: '200px'}}>
+                    {showSelector && filteredElements.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
                 </Gallery>
             </PageSection>
         </Modal>