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

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

commit 444ff792f38c3148228b06b8a7651d88303ca2c2
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Mon Aug 24 17:58:29 2026 -0400

    Karavan-app UI Page Command Palette
---
 .../src/ui/command-palette/CommandEventBus.ts      |  25 +++
 .../CommandPaletteCamelStepsGallery.tsx            |  49 ++++++
 .../command-palette/CommandPaletteDslElement.tsx   |  66 +++++++
 .../command-palette/CommandPaletteDslSelected.tsx  |  19 ++
 .../CommandPaletteDslSelectedCard.tsx              |  75 ++++++++
 .../ui/command-palette/CommandPaletteEditor.css    |  50 ++++++
 .../ui/command-palette/CommandPaletteEditor.tsx    |  87 ++++++++++
 .../CommandPaletteFilenameInput.tsx                |  47 +++++
 .../ui/command-palette/CommandPaletteFooter.tsx    |  58 +++++++
 .../src/ui/command-palette/CommandPaletteModal.tsx |  60 +++++++
 .../src/ui/command-palette/CommandPalettePanel.css | 191 +++++++++++++++++++++
 .../src/ui/command-palette/CommandPalettePanel.tsx |  58 +++++++
 .../src/ui/command-palette/CommandPaletteUtils.tsx |  32 ++++
 .../src/ui/command-palette/useCommandHook.tsx      | 161 +++++++++++++++++
 .../ui/command-palette/useCommandPaletteStore.ts   | 104 +++++++++++
 15 files changed, 1082 insertions(+)

diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandEventBus.ts 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandEventBus.ts
new file mode 100644
index 00000000..2573af15
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandEventBus.ts
@@ -0,0 +1,25 @@
+import {Subject} from 'rxjs';
+import {useEffect} from "react";
+
+const cmdKEvents = new Subject<void>();
+
+export const CommandEventBus = {
+    sendCmdK: () => cmdKEvents.next(),
+    onCmdK: () => cmdKEvents.asObservable(),
+};
+
+export function useGlobalShortcuts() {
+    useEffect(() => {
+        const handleKeyDown = (event: KeyboardEvent) => {
+            if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() 
=== 'k') {
+                event.preventDefault();
+                CommandEventBus.sendCmdK();
+            }
+        };
+
+        document.addEventListener('keydown', handleKeyDown);
+        return () => {
+            document.removeEventListener('keydown', handleKeyDown);
+        };
+    }, []);
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteCamelStepsGallery.tsx
 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteCamelStepsGallery.tsx
new file mode 100644
index 00000000..aa85774e
--- /dev/null
+++ 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteCamelStepsGallery.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import './CommandPalettePanel.css';
+import {CamelUi} from "@designer/utils/CamelUi";
+import {DslMetaModel} from "@designer/utils/DslMetaModel";
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {CommandPaletteDslElement} from "./CommandPaletteDslElement";
+import {Gallery} from "@patternfly/react-core";
+
+export function CommandPaletteCamelStepsGallery() {
+    const selectedDsl = useCommandPaletteStore((s) => s.selectedDsl);
+    const elements = useCommandPaletteStore((s) => s.elements);
+    const filter = useCommandPaletteStore((s) => s.filter);
+
+    // 1. Filter elements using the existing logic
+    const filteredElements: DslMetaModel[] = selectedDsl
+        ? [selectedDsl]
+        : elements.filter(d => CamelUi.checkFilter(d, filter));
+
+
+    // 2. Sort the filtered elements based on keyword order in the filter
+    if (!selectedDsl && filter && filter.trim().length > 0) {
+        // Extract individual keywords in the exact order they were typed
+        const keywords = filter.toLowerCase().split(/\s+/).filter(Boolean);
+
+        // Helper function to assign a rank based on the earliest keyword match
+        const getRank = (dsl: DslMetaModel) => {
+            // Combine searchable fields into a single lowercase string
+            const searchText = `${dsl.name || ''} ${dsl.title || ''} 
${dsl.description || ''}`.toLowerCase();
+
+            // Find the index of the FIRST keyword in the filter string that 
matches this element
+            const matchIndex = keywords.findIndex(kw => 
searchText.includes(kw));
+
+            // If matched, return its index (0 is best rank). If no match 
(unlikely since it passed filter), put it at the end.
+            return matchIndex === -1 ? keywords.length : matchIndex;
+        };
+
+        // Sort elements: lower rank (earlier keyword match) comes first
+        filteredElements.sort((a, b) => getRank(a) - getRank(b));
+    }
+
+    return (
+            <Gallery key={"gallery"} hasGutter className="dsl-gallery" 
minWidths={{default: '170px'}}>
+                {filteredElements.map((dsl: DslMetaModel, index: number) =>
+                    <CommandPaletteDslElement key={dsl.name + ":" + index} 
dsl={dsl} index={index}/>
+                )}
+            </Gallery>
+
+    );
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslElement.tsx
 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslElement.tsx
new file mode 100644
index 00000000..e1b59cc9
--- /dev/null
+++ 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslElement.tsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import {Badge, capitalize, Card, CardBody, CardHeader, HelperText, 
HelperTextItem, Tooltip} from '@patternfly/react-core';
+import './CommandPalettePanel.css';
+import '@designer/property/property/ComponentPropertyField.css';
+import {CamelUi} from "@designer/utils/CamelUi";
+import {DslMetaModel} from "@designer/utils/DslMetaModel";
+import {useCommandHook} from "./useCommandHook";
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {highlightText} from "./CommandPaletteUtils";
+
+interface Props {
+    dsl: DslMetaModel;
+    index: number;
+}
+
+export function CommandPaletteDslElement(props: Props) {
+    const {dsl, index} = props;
+    const {dslCardClick} = useCommandHook();
+    const filter = useCommandPaletteStore((s) => s.filter);
+    const navigation = dsl.navigation === 'eip' ? 'Processor' : 
capitalize(dsl.navigation);
+    const classNameBadge = "navigation-label label-" + dsl.navigation + 
((dsl.navigation === 'eip' || dsl?.supportLevel.toLowerCase() === 'stable') ? 
'' : '-preview');
+
+    // Add a keyboard handler for Enter and Space keys
+    const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
+        if (event.key === 'Enter' || event.key === ' ') {
+            event.preventDefault(); // Prevent page scrolling if Space is 
pressed
+            dslCardClick(event, dsl);
+        }
+    };
+
+    return (
+        <Card
+            key={dsl.dsl + index}
+            isCompact
+            className="dsl-card"
+            style={{width: '100%'}}
+            tabIndex={0}
+            onKeyDown={handleKeyDown}
+            onClick={e => dslCardClick(e, dsl)}>
+            <CardHeader>
+                <Tooltip content={navigation} position="right">
+                    <Badge 
className={classNameBadge}>{navigation?.substring(0, 1)}</Badge>
+                </Tooltip>
+                <div className="dsl-element">
+                    <div className={"header"}>
+                        <div className={"icon-wrapper"}>
+                            {CamelUi.getIconForDsl(dsl)}
+                        </div>
+                        <p className='dsl-element-title'>
+                            {highlightText(dsl.title, filter)}
+                        </p>
+                    </div>
+                </div>
+            </CardHeader>
+            <CardBody onClick={e => dslCardClick(e, dsl)}>
+                <div className="dsl-element-body-description">
+                    <Tooltip content={dsl.description}>
+                        <HelperText>
+                            <HelperTextItem 
className={"dsl-element-text-helper"}>{highlightText(dsl.description, 
filter)}</HelperTextItem>
+                        </HelperText>
+                    </Tooltip>
+                </div>
+            </CardBody>
+        </Card>
+    );
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslSelected.tsx
 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslSelected.tsx
new file mode 100644
index 00000000..42124ef6
--- /dev/null
+++ 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslSelected.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import {Bullseye} from '@patternfly/react-core';
+import './CommandPalettePanel.css';
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {CommandPaletteDslSelectedCard} from "./CommandPaletteDslSelectedCard";
+
+
+export function CommandPaletteDslSelected() {
+
+    const selectedDsl = useCommandPaletteStore((s) => s.selectedDsl);
+
+    return (
+        <Bullseye>
+            <div style={{display: 'flex', flexDirection: 'column', 
justifyContent: 'center', alignItems: 'center', gap: '16px', minWidth: 
"400px"}}>
+                <CommandPaletteDslSelectedCard key={selectedDsl.name} 
dsl={selectedDsl} index={0}/>
+            </div>
+        </Bullseye>
+    )
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslSelectedCard.tsx
 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslSelectedCard.tsx
new file mode 100644
index 00000000..54861ce4
--- /dev/null
+++ 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteDslSelectedCard.tsx
@@ -0,0 +1,75 @@
+import React from 'react';
+import {Card, CardBody, CardHeader, Form,} from '@patternfly/react-core';
+import './CommandPalettePanel.css';
+import '@designer/property/property/ComponentPropertyField.css';
+import {CamelUi} from "@designer/utils/CamelUi";
+import {DslMetaModel} from "@designer/utils/DslMetaModel";
+import {ComponentApi} from "@core/api/ComponentApi";
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {ComponentPropertyField} from 
"@designer/property/property/ComponentPropertyField";
+import {ExpressionEditor} from 
"@designer/property/expression/ExpressionEditor";
+import {ComponentProperty} from "@core/model/ComponentModels";
+import {highlightText} from "./CommandPaletteUtils";
+
+interface Props {
+    dsl: DslMetaModel,
+    index: number
+}
+
+export function CommandPaletteDslSelectedCard(props: Props) {
+
+    const {dsl, index} = props;
+    const filter = useCommandPaletteStore((s) => s.filter);
+    const showProperties = useCommandPaletteStore((s) => s.showProperties);
+    const selectedDsl = useCommandPaletteStore((s) => s.selectedDsl);
+    const setSelectedDsl = useCommandPaletteStore((s) => s.setSelectedDsl);
+
+    const componentProperties = showProperties && dsl?.uri
+        ? ComponentApi.getComponentProperties(dsl.uri, 'consumer').filter(p => 
p.kind === 'path')
+        : [];
+
+    function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>): void {
+        if (event.key === 'Escape') {
+            close();
+        }
+    }
+
+    return (
+        <Card key={dsl.dsl + index} className="dsl-card" style={{width: 
'100%'}} tabIndex={0} onKeyDown={onKeyDown}>
+            <CardHeader className="header-labels">
+                <div className="dsl-element">
+                    <div className={"header"}>
+                        {CamelUi.getIconForDsl(dsl)}
+                        <p className='dsl-element-title'>
+                            {highlightText(dsl.title, filter)}
+                        </p>
+                    </div>
+                </div>
+            </CardHeader>
+            {showProperties && selectedDsl &&
+                <CardBody className="dsl-card-body-properties">
+                    <Form autoComplete="off" className='properties'>
+                        {componentProperties.map((kp: ComponentProperty) =>
+                            <ComponentPropertyField
+                                hideConfigSelector={true}
+                                key={kp.name}
+                                property={kp}
+                                value={selectedDsl.properties?.[kp.name]}
+                                expressionEditor={ExpressionEditor}
+                                onParameterChange={(parameter, value, 
pathParameter, newRoute) => {
+                                    setSelectedDsl({
+                                        ...selectedDsl,
+                                        properties: {
+                                            ...(selectedDsl.properties ?? {}),
+                                            [parameter]: value
+                                        }
+                                    });
+                                }}
+                            />
+                        )}
+                    </Form>
+                </CardBody>
+            }
+        </Card>
+    )
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteEditor.css 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteEditor.css
new file mode 100644
index 00000000..11d26a6f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteEditor.css
@@ -0,0 +1,50 @@
+.command-palette-editor {
+    position: relative;
+    width: 100%;
+    padding-top: 4px;
+    border-radius: 8px;
+    overflow: hidden;
+
+    .empty-editor-overlay {
+        position: absolute;
+        top: 4px;
+        left: 12px;
+        color: var(--pf-t--global--text--color--regular);
+        pointer-events: none;
+        font-size: 12px;
+        font-family: Menlo, Monaco, "Courier New", monospace;
+        z-index: 10;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        width: 100%;
+    }
+}
+
+.command-palette-editor::after {
+    content: "";
+    position: absolute;
+    /* Added a fallback of 0px just in case the PatternFly variable is missing 
*/
+    inset: var(--pf-v6-c-button--border--offset, 0px);
+    pointer-events: none;
+    border: 2px solid transparent;
+
+    /* Good practice to inherit border-radius so the gradient hugs the corners 
*/
+    border-radius: 8px;
+    transition: inherit;
+
+    /* The gradient border colors */
+    background: linear-gradient(
+            to right,
+            rgba(0, 102, 204, 1) 0%,
+            rgba(67, 148, 229, 1) 25%,
+            rgb(219, 91, 4) 100%
+    ) border-box;
+
+    /* Correct Masking implementation */
+    -webkit-mask:
+            linear-gradient(#fff 0 0) padding-box,
+            linear-gradient(#fff 0 0);
+    -webkit-mask-composite: destination-out;
+    mask-composite: exclude;
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteEditor.tsx 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteEditor.tsx
new file mode 100644
index 00000000..d3eb9c2f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteEditor.tsx
@@ -0,0 +1,87 @@
+import React, {useEffect, useRef, useState} from 'react';
+import './CommandPaletteEditor.css';
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {useDebounceValue} from 'usehooks-ts';
+import {MonacoEditorWrapper} from "@developer/monaco/MonacoEditorWrapper";
+import type * as monaco from "monaco-editor";
+
+const editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
+    minimap: { enabled: false },
+    scrollBeyondLastLine: false,
+    scrollbar: {
+        useShadows: false,
+        vertical: 'auto',
+        horizontal: 'hidden'
+    },
+    selectOnLineNumbers: true,
+    automaticLayout: true,
+    lineNumbers: "off",
+    folding: false,
+    lineNumbersMinChars: 0,
+    showUnused: false,
+    fontSize: 12,
+    fixedOverflowWidgets: false,
+    wordWrap: "on",
+    wordBasedSuggestions: "off",
+    quickSuggestions: false,
+    snippetSuggestions: "none",
+    suggestOnTriggerCharacters: false,
+    suggest: {
+        showKeywords: false,
+        showStatusBar: false,
+        showIcons: false,
+        preview: false,
+        showSnippets: false,
+    },
+};
+
+const LINE_HEIGHT = 18;
+const MIN_LINES = 3;
+const MIN_HEIGHT = LINE_HEIGHT * MIN_LINES;
+
+function clampHeight(contentHeight: number, minHeight: number, maxHeight: 
number) {
+    return Math.min(maxHeight, Math.max(minHeight, contentHeight));
+}
+
+export function CommandPaletteEditor() {
+    const parentDsl = useCommandPaletteStore((s) => s.parentDsl);
+    const setStoreFilter = useCommandPaletteStore((s) => s.setFilter);
+    const [editorReady, setEditorReady] = useState(false);
+    const [localFilter, setLocalFilter] = useState('');
+    const [debouncedFilter] = useDebounceValue(localFilter, 300);
+
+    const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
+    const hasAppliedTemplate = useRef(false);
+    const isPastedContent = useRef(false);
+
+    useEffect(() => {
+        setStoreFilter(debouncedFilter);
+    }, [debouncedFilter, setStoreFilter]);
+
+    function onChange(value: string) {
+        setLocalFilter(value || '');
+    }
+
+    const elementType = parentDsl === undefined || parentDsl === '' ? 
'starting' : 'next';
+    const camelStepsPlaceholder = "Search " + elementType + " step from the 
list below 👇";
+    let placeholderText = camelStepsPlaceholder;
+
+    // The editor always starts empty - templates are applied through 
executeEdits
+    const isEditorEmpty = localFilter.trim().length === 0;
+
+    return (
+        <div className={"command-palette-editor"}>
+            {isEditorEmpty && (
+                <div className={"empty-editor-overlay"}>{placeholderText}</div>
+            )}
+            <MonacoEditorWrapper
+                key={'modal-special-focus'}
+                height={`${MIN_HEIGHT}px`}
+                language="markdown"
+                editorOptions={editorOptions}
+                initialCode={""}
+                onChange={(value) => onChange(value?.toString() || "")}
+            />
+        </div>
+    );
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteFilenameInput.tsx
 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteFilenameInput.tsx
new file mode 100644
index 00000000..2732f7a4
--- /dev/null
+++ 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteFilenameInput.tsx
@@ -0,0 +1,47 @@
+import React, {useEffect} from 'react';
+import {Content, TextInputGroup, TextInputGroupMain, TextInputGroupUtilities} 
from '@patternfly/react-core';
+import './CommandPalettePanel.css';
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {useCommandHook} from "./useCommandHook";
+import {ExclamationCircleIcon} from "@patternfly/react-icons";
+
+export function CommandPaletteFilenameInput() {
+
+    const {validated, generateRouteFileName} = useCommandHook()
+
+    const selectedDsl = useCommandPaletteStore((s) => s.selectedDsl);
+    const fileName = useCommandPaletteStore((s) => s.fileName);
+    const setFileName = useCommandPaletteStore((s) => s.setFileName);
+
+    useEffect(() => {
+        if (selectedDsl && !fileName) {
+            const f = generateRouteFileName(selectedDsl)
+            setFileName(f)
+        } else if (!selectedDsl && !fileName) {
+            const f = generateRouteFileName(undefined)
+            setFileName(f)
+        }
+    }, [selectedDsl]);
+
+    return (
+        <div style={{display: 'flex', flexDirection: 'row', alignItems: 
'center', justifyContent: 'space-between', gap: '8px'}}>
+            <Content style={{textWrap: 'nowrap', margin: 0, fontWeight: 
'bold'}} component='p'>File name: </Content>
+            <TextInputGroup className="search">
+                <TextInputGroupMain
+                    style={{textAlign: 'right'}}
+                    value={fileName ?? ''}
+                    onChange={(_event, value) => setFileName(value)}
+                    type="text"
+                    aria-label="invalid text input example"
+                />
+                <TextInputGroupUtilities>
+                    <Content style={{textWrap: 'nowrap', padding: '3px'}} 
component='p'>.camel.yaml</Content>
+                </TextInputGroupUtilities>
+                <TextInputGroupUtilities>
+                    {!validated() &&
+                        <ExclamationCircleIcon 
color='var(--pf-t--global--icon--color--status--danger--default)' 
style={{textWrap: 'nowrap', marginRight: '3px'}}/>}
+                </TextInputGroupUtilities>
+            </TextInputGroup>
+        </div>
+    )
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteFooter.tsx 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteFooter.tsx
new file mode 100644
index 00000000..0c744022
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteFooter.tsx
@@ -0,0 +1,58 @@
+import React, {useEffect} from 'react';
+import {Button, ModalFooter} from '@patternfly/react-core';
+import './CommandPalettePanel.css';
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {useCommandHook} from "./useCommandHook";
+import {CommandPaletteFilenameInput} from "./CommandPaletteFilenameInput";
+import {DslMetaModel} from "@designer/utils/DslMetaModel";
+import {CommandEventBus} from "./CommandEventBus";
+
+interface Props {
+    onClose?: () => void
+    onBeforeSave?: (dsl: DslMetaModel) => void
+}
+
+export function CommandPaletteFooter(props: Props) {
+
+    const {onClose, onBeforeSave} = props;
+    const {afterSelect, validated, selectedDsl, isTopology} = useCommandHook();
+    const showProperties = useCommandPaletteStore((s) => s.showProperties);
+    const setSelectedDsl = useCommandPaletteStore((s) => s.setSelectedDsl);
+
+    function getButtons() {
+        return (
+            <div style={{display: 'flex', flexDirection: 'row', 
justifyContent: 'flex-end', gap: 6}}>
+                {/*<Button variant='link' isDanger onClick={_ => {*/}
+                {/*    close();*/}
+                {/*    onClose?.();*/}
+                {/*}}>Close</Button>*/}
+                {showProperties && selectedDsl &&
+                    <Button variant='secondary'
+                            onClick={_ => setSelectedDsl(undefined)}
+                    >
+                        Back
+                    </Button>
+                }
+                {showProperties && selectedDsl &&
+                    <Button variant='primary'
+                            isDisabled={!validated()}
+                            onClick={_ => {
+                                onBeforeSave?.(selectedDsl);
+                                afterSelect(selectedDsl);
+                            }}
+                    >
+                        Save
+                    </Button>
+                }
+            </div>
+        )
+    }
+
+    return (
+        <ModalFooter className="dsl-footer">
+            {isTopology && selectedDsl && <CommandPaletteFilenameInput/>}
+            <div style={{flex: 1}}></div>
+            {getButtons()}
+        </ModalFooter>
+    )
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteModal.tsx 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteModal.tsx
new file mode 100644
index 00000000..f0f96d2c
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteModal.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import {Button, ClipboardCopy, Content, Divider, Modal, ModalBody, 
ModalHeader} from '@patternfly/react-core';
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {useCommandHook} from "./useCommandHook";
+import {CommandPalettePanel} from "./CommandPalettePanel";
+import {CommandPaletteFooter} from "./CommandPaletteFooter";
+import "./CommandPalettePanel.css"
+import {TimesIcon} from "@patternfly/react-icons";
+
+export function CommandPaletteModal() {
+
+    const {afterSelect, close, isFileSelected, file, project} = 
useCommandHook()
+    const showPalette = useCommandPaletteStore((s) => s.showPalette);
+    const showProperties = useCommandPaletteStore((s) => s.showProperties);
+    const selectedDsl = useCommandPaletteStore((s) => s.selectedDsl);
+    const filter = useCommandPaletteStore((s) => s.filter);
+
+    function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>): void {
+        if (event.key === 'Enter' && showProperties && selectedDsl) {
+            afterSelect(selectedDsl)
+        } else if (event.key === 'Escape') {
+            close();
+        }
+    }
+
+    const currentContext = <ClipboardCopy
+        className="filename-text-clipboard"
+        hoverTip="Copy"
+        clickTip="Copied"
+        variant="inline-compact"
+        isCode
+    >
+        {file?.name || project?.projectId}
+    </ClipboardCopy>
+
+    return (
+        <Modal
+            width={'70%'}
+            className='command-palette command-palette-modal'
+            position="top"
+            isOpen={showPalette}
+            onKeyDown={onKeyDown}
+        >
+            <ModalHeader className={"command-palette-modal-header"}>
+                <Content style={{margin: 0}} component={"h6"}>Select 
for</Content>
+                {(isFileSelected || project?.projectId)  && currentContext}
+                <div style={{display:"flex", justifyContent: "flex-end", 
alignItems: "center", flex: 1}}>
+                <Button variant='link' isDanger className={"close-button"} 
onClick={_ => close()}>
+                    <TimesIcon/>
+                </Button>
+                </div>
+            </ModalHeader>
+            <ModalBody style={{padding: 0}}>
+                <CommandPalettePanel/>
+            </ModalBody>
+            <Divider/>
+            <CommandPaletteFooter onClose={() => close()}/>
+        </Modal>
+    )
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPalettePanel.css 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPalettePanel.css
new file mode 100644
index 00000000..799cd324
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandPalettePanel.css
@@ -0,0 +1,191 @@
+.command-palette-modal {
+    min-height: 30%;
+    overflow: hidden;
+    border-radius: var(--pf-t--global--border--radius--medium);
+    .command-palette-modal-header {
+        padding: 6px 6px 0 16px;
+        /*height: 1.7em;*/
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        position: relative;
+        gap: 6px;
+        .filename-text-clipboard {
+            background-color: transparent;
+        }
+    }
+}
+
+.command-palette .pf-v6-c-form-control > :is(input, select, textarea) {
+    padding-inline-start: var(--pf-t--global--spacer--sm);
+    padding-inline-end: var(--pf-t--global--spacer--sm);
+}
+
+.command-palette .command-palette-body {
+    display: block;
+    padding-top: 1em;
+    padding-inline-start: 1em;
+    padding-inline-end: 1em;
+    padding-bottom: 1em;
+    flex-basis: auto;
+    flex-grow: 1;
+    flex-shrink: 1;
+    overflow-x: hidden;
+    overflow-y: auto;
+}
+
+.command-palette .dsl-element {
+    position: relative;
+    cursor: pointer;
+    display: flex;
+    flex-direction: column;
+    align-items: stretch;
+    text-wrap: nowrap;
+    gap: 16px;
+    padding: 4px 4px;
+}
+
+.command-palette .dsl-element .header {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    gap: 2px;
+}
+
+.command-palette .dsl-element .icon-wrapper {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 30px; /* Increased slightly to give the icon breathing room */
+    width: 30px;  /* Width MUST match height for a perfect circle */
+    border-radius: 50%; /* This creates the circle shape */
+    margin-right: 6px;
+    flex-shrink: 0; /* Prevents the circle from squishing if the container 
gets tight */
+}
+
+.pf-v6-theme-dark .command-palette .dsl-element .icon-wrapper {
+    background-color: var(--pf-t--global--text--color--subtle);
+}
+
+.command-palette .dsl-element .icon {
+    height: 20px;
+    width: auto;
+    border: none;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    user-select: none;
+}
+
+.command-palette .dsl-element .highlight-match {
+    color: var(--platform-color);
+}
+
+.command-palette .dsl-element .dsl-element-title {
+    color: var(--pf-t--global--text--color--regular);
+    font-weight: bold;
+    text-wrap: wrap;
+}
+
+.command-palette .dsl-list {
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+    width: 100%;
+}
+.command-palette .dsl-footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    gap: 6px;
+    width: 100%;
+    padding-block-start: 1em;
+    padding-block-end: 1em;
+    padding-inline-start: 1em;
+    padding-inline-end: 1em;
+}
+
+.command-palette .command-palette-header {
+    display: flex;
+    flex-direction: column;
+    flex-basis: auto;
+    flex-shrink: 1;
+    padding-block-start: 1em;
+    padding-inline-start: 1em;
+    padding-inline-end: 1em;
+}
+
+.command-palette .pf-v6-c-modal-box__close {
+    inset-block-start: -2px;
+    inset-inline-end: -2px;
+    overflow: hidden;
+}
+
+.command-palette .dsl-card .properties .pf-v6-c-form__group-label,
+.command-palette .dsl-card .properties .pf-v6-c-form__label {
+    display: flex;
+}
+
+.command-palette .pf-chatbot__message-bar .sdx-design-button {
+    background: var(--pf-t--global--background--color--primary--default);
+    svg {
+        fill: var(--platform-color);
+    }
+}
+.command-palette .pf-chatbot__message-bar .sdx-design-button::after {
+    position: absolute;
+    inset: var(--pf-v6-c-button--border--offset);
+    pointer-events: none;
+    content: "";
+    border: 2px solid transparent;
+    border-radius: 9999px; /* Overriding the inherit to force stadium shape */
+    transition: inherit;
+
+    background-image: linear-gradient(
+            to right,
+            rgba(0, 102, 204, 1) 0%,
+            rgba(67, 148, 229, 1) 25%,
+            rgb(219, 91, 4) 100%
+    );
+    background-origin: border-box;
+
+    -webkit-mask: linear-gradient(#000 0 0) padding-box exclude, 
linear-gradient(#000 0 0) border-box;
+    mask: linear-gradient(#000 0 0) padding-box exclude, linear-gradient(#000 
0 0) border-box;
+}
+
+.command-palette .dsl-gallery {
+    .dsl-card {
+        cursor: pointer;
+        .pf-v6-c-card__header {
+            padding: 0.5em;
+            .navigation-label {
+                position: absolute;
+                right: 4px;
+                top: 4px;
+                padding: 0;
+                margin: 0;
+                width: 8px;
+                min-width: 16px;
+            }
+            .navigation-label::after {
+                border: none;
+            }
+        }
+        .pf-v6-c-card__body {
+            .dsl-element-body-description {
+                overflow: hidden;
+                .pf-v6-c-helper-text__item-text {
+                    display: -webkit-box;
+                    -webkit-box-orient: vertical;
+                    -webkit-line-clamp: 2;
+                    color: var(--pf-t--global--text--color--subtle);
+                }
+            }
+        }
+    }
+}
+
+.command-palette .decorationsOverviewRuler {
+    visibility: hidden;
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPalettePanel.tsx 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPalettePanel.tsx
new file mode 100644
index 00000000..82f3baae
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandPalettePanel.tsx
@@ -0,0 +1,58 @@
+import React, {useEffect, useState} from 'react';
+import {Skeleton} from '@patternfly/react-core';
+import './CommandPalettePanel.css';
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {CommandPaletteEditor} from "./CommandPaletteEditor";
+import {useCommandHook} from "./useCommandHook";
+import {CommandPaletteCamelStepsGallery} from 
"./CommandPaletteCamelStepsGallery";
+import {CommandPaletteDslSelected} from "./CommandPaletteDslSelected";
+
+export function CommandPalettePanel() {
+
+    const {afterSelect, setAllElements, close, selectedDsl, showCamelSteps} = 
useCommandHook()
+    const showProperties = useCommandPaletteStore((s) => s.showProperties);
+    const setShowProperties = useCommandPaletteStore((s) => 
s.setShowProperties);
+    const setSelectedDsl = useCommandPaletteStore((s) => s.setSelectedDsl);
+    const [ready, setReady] = useState<boolean>(true);
+
+    useEffect(() => {
+        setAllElements();
+        setReady(true);
+        setShowProperties(false);
+        setSelectedDsl(undefined);
+        return () => {
+            setShowProperties(false);
+            setSelectedDsl(undefined);
+        }
+    }, []);
+
+    function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>): void {
+        if (event.key === 'Enter' && showProperties && selectedDsl) {
+            afterSelect(selectedDsl)
+        } else if (event.key === 'Escape') {
+            close();
+        }
+    }
+
+    function getNotReady() {
+        return !ready && [1, 2, 3, 4, 5, 6, 7, 8, 9].map(i =>
+            <React.Fragment key={i}>
+                <Skeleton key={i} width={i * 10 + '%'} 
screenreaderText="Loading..."/>
+                <br/>
+            </React.Fragment>
+        )
+    }
+
+    return (
+        <div onKeyDown={onKeyDown} className={"command-palette"} 
style={{display: 'flex', flexDirection: 'column', height: "100%", width: 
"100%"}}>
+            <div className="command-palette-header">
+                {ready && <CommandPaletteEditor/>}
+            </div>
+            <div className={"command-palette-body"}>
+                {getNotReady()}
+                {ready && showCamelSteps && <CommandPaletteCamelStepsGallery/>}
+                {ready && showProperties && selectedDsl && 
<CommandPaletteDslSelected/>}
+            </div>
+        </div>
+    )
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteUtils.tsx 
b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteUtils.tsx
new file mode 100644
index 00000000..180ccdbe
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/CommandPaletteUtils.tsx
@@ -0,0 +1,32 @@
+// Helper function to escape special characters for the regex
+import React, {ReactNode} from "react";
+
+export const escapeRegExp = (text: string) => {
+    return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+};
+
+// Helper function to highlight filter keywords in a given text
+export const highlightText = (text: string, filter: string): ReactNode => {
+    if (!filter || !filter.trim()) return text;
+
+    // Split filter into individual words and remove empty strings
+    const keywords = filter.split(/\s+/).filter(Boolean);
+    if (keywords.length === 0) return text;
+
+    // Create a regex to match any of the keywords (case-insensitive)
+    const escapedKeywords = keywords.map(escapeRegExp);
+    const regex = new RegExp(`(${escapedKeywords.join('|')})`, 'gi');
+
+    // Split text by the regex (the capturing group '()' ensures matches are 
kept in the array)
+    const parts = text.split(regex);
+
+    return parts.map((part, i) => {
+        // If the current part matches any of our keywords, wrap it in a span 
with the special class
+        const isMatch = keywords.some(kw => kw.toLowerCase() === 
part.toLowerCase());
+        return isMatch ? (
+            <span key={i} className="highlight-match">{part}</span>
+        ) : (
+            <React.Fragment key={i}>{part}</React.Fragment>
+        );
+    });
+};
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/useCommandHook.tsx 
b/karavan-app/src/main/webui/src/ui/command-palette/useCommandHook.tsx
new file mode 100644
index 00000000..de08004e
--- /dev/null
+++ b/karavan-app/src/main/webui/src/ui/command-palette/useCommandHook.tsx
@@ -0,0 +1,161 @@
+import {useCommandPaletteStore} from "./useCommandPaletteStore";
+import {DslMetaModel} from "@designer/utils/DslMetaModel";
+import {FILE_WORDS_SEPARATOR, KARAVAN_DOT_EXTENSION, KARAVAN_FILENAME, 
MARKDOWN_EXTENSION} from "@core/contants";
+import {useFilesStore, useFileStore, useProjectStore} from 
"@stores/ProjectStore";
+import React from "react";
+import {CamelUi} from "@designer/utils/CamelUi";
+import {v4 as uuidv4} from "uuid";
+import {toSpecialRouteId} from "@designer/utils/ValidatorUtils";
+import {ComponentApi} from "@core/api/ComponentApi";
+import {ProjectFunctionHook} from "@page-project/ProjectFunctionHook";
+import {useRouteDesignerHook} from "@designer/route/useRouteDesignerHook";
+import {useUIStore} from "@stores/useUIStore";
+import {useTemplatesStore} from "@stores/SettingsStore";
+
+export function useCommandHook() {
+
+    const parentDsl = useCommandPaletteStore((s) => s.parentDsl);
+    const showSteps = useCommandPaletteStore((s) => s.showSteps);
+    const parentId = useCommandPaletteStore((s) => s.parentId);
+    const setShowSelector = useCommandPaletteStore((s) => s.setShowPalette);
+    const selectedPosition = useCommandPaletteStore((s) => s.selectedPosition);
+    const setShowProperties = useCommandPaletteStore((s) => 
s.setShowProperties);
+    const setSelectedDsl = useCommandPaletteStore((s) => s.setSelectedDsl);
+    const isRouteTemplate = useCommandPaletteStore((s) => s.isRouteTemplate);
+    const setElements = useCommandPaletteStore((s) => s.setElements);
+    const setStoreFilter = useCommandPaletteStore((state) => state.setFilter);
+    const filter = useCommandPaletteStore((state) => state.filter);
+    const setFileName = useCommandPaletteStore((state) => state.setFileName);
+    const selectedDsl = useCommandPaletteStore((s) => s.selectedDsl);
+    const fileName = useCommandPaletteStore((state) => state.fileName);
+    const showProperties = useCommandPaletteStore((s) => s.showProperties);
+    const files = useFilesStore((s) => s.files);
+    const file = useFileStore((s) => s.file);
+    const project = useProjectStore((s) => s.project);
+    const tabIndex = useProjectStore(s => s.tabIndex);
+    const templateFiles = useTemplatesStore((s) => s.templateFiles);
+    const pageId = useUIStore(s => s.pageId);
+    const {createNewRouteFile} = ProjectFunctionHook();
+    const {onAddNewRouteStep} = useRouteDesignerHook();
+    const onDslSelect = file === undefined ? createNewRouteFile : 
onAddNewRouteStep;
+
+    function afterSelect(dsl: DslMetaModel) {
+        setStoreFilter('');
+        setShowSelector(false);
+        onDslSelect(dsl, parentId, selectedPosition, fileName);
+        setFileName(undefined);
+    }
+
+    function validated(): boolean {
+        return files.find(f => f.name === 
`${fileName}${KARAVAN_DOT_EXTENSION.CAMEL_YAML}`) === undefined;
+    }
+
+    function close() {
+        setStoreFilter('');
+        setFileName(undefined);
+        setShowSelector(false);
+    }
+
+    function dslCardClick(evt: React.MouseEvent | 
React.KeyboardEvent<HTMLDivElement>, dsl: any) {
+        evt.stopPropagation();
+        if (parentId?.length > 0) {
+            afterSelect(dsl as DslMetaModel);
+        } else {
+            setSelectedDsl(dsl as DslMetaModel);
+            setShowProperties(true)
+        }
+    }
+
+    function setAllElements() {
+        const blockedComponents = ComponentApi.getBlockedComponentNames();
+        const eipE = CamelUi.getSelectorModelsForParentFiltered(parentDsl, 
'eip', showSteps);
+        const cE = CamelUi.getSelectorModelsForParentFiltered(parentDsl, 
'component', showSteps)
+            .filter(dsl => (!blockedComponents.includes(dsl.uri || dsl.name)));
+        const e: DslMetaModel[] = [];
+        if (parentDsl !== undefined) {
+            e.push(...eipE)
+        }
+        e.push(...cE)
+        const kE = CamelUi.getSelectorModelsForParentFiltered(parentDsl, 
'kamelet', showSteps);
+        e.push(...kE);
+        setElements(e);
+    }
+
+    function generateParamUri(dsl: DslMetaModel) {
+        const uuid = uuidv4().substring(0, 3)
+        const uri = dsl.uri + FILE_WORDS_SEPARATOR +
+            (dsl.properties && Object.keys(dsl.properties).length > 0
+                ? Object.values(dsl.properties).join(FILE_WORDS_SEPARATOR)
+                : uuid);
+        return uri
+            .replace(/[^a-zA-Z0-9]/g, '-')
+            .replace(/-+/g, '-')
+            .replace(/^-|-$/g, '');
+    }
+
+    function generateRouteFileName(dsl: DslMetaModel): string {
+        if (dsl === undefined) {
+            return "from-" + uuidv4().substring(0, 3);
+        }
+        const paramsUri = generateParamUri(dsl);
+        const fullUri = `${FILE_WORDS_SEPARATOR}${paramsUri}`;
+        if (isRouteTemplate) {
+            return 
toSpecialRouteId(`${FILE_WORDS_SEPARATOR}${fullUri}-route-template`);
+        } else {
+            return toSpecialRouteId(`${FILE_WORDS_SEPARATOR}${fullUri}`);
+        }
+    }
+
+    const isFileSelected = file?.name !== undefined && project?.projectId !== 
undefined;
+    const isFileCamel = isFileSelected && 
file?.name.endsWith(KARAVAN_DOT_EXTENSION.CAMEL_YAML);
+    const isFileGroovy = isFileSelected && 
file?.name.endsWith(KARAVAN_DOT_EXTENSION.GROOVY);
+    const isApplicationProperties = isFileSelected && file?.name === 
KARAVAN_FILENAME.APP_PROPERTIES;
+    const isFileMarkdown = isFileSelected && 
file?.name.endsWith(MARKDOWN_EXTENSION);
+    const isTopology = tabIndex === 'topology' && !isFileSelected && 
project?.projectId !== undefined;
+    const isDashboard = pageId === 'dashboard' && !isFileSelected && 
project?.projectId === undefined;
+    const isProjects = pageId === 'projects';
+    const isLogOpen = project?.projectId !== undefined && tabIndex === 'log';
+
+    const showCamelSteps = React.useMemo(() => {
+        if (showProperties) {
+            return false;
+        }
+        if (isDashboard || isTopology || isFileCamel) {
+            return true;
+        }
+        if (isProjects && !isFileSelected) {
+            return true;
+        }
+        return false;
+    }, [isDashboard,
+        isProjects,
+        isTopology,
+        isFileCamel,
+        isLogOpen,
+        isApplicationProperties,
+        isFileMarkdown,
+        showProperties,
+        file?.name,
+        project?.projectId,
+        ]);
+
+
+    return {
+        afterSelect, validated, close, dslCardClick, setAllElements, 
generateRouteFileName, onDslSelect, selectedDsl,
+        isFileSelected,
+        isFileCamel,
+        isFileGroovy,
+        isFileMarkdown,
+        isTopology,
+        isDashboard,
+        isLogOpen,
+        isProjects,
+        file,
+        files,
+        project,
+        pageId,
+        tabIndex,
+        isApplicationProperties,
+        showCamelSteps,
+    };
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/ui/command-palette/useCommandPaletteStore.ts 
b/karavan-app/src/main/webui/src/ui/command-palette/useCommandPaletteStore.ts
new file mode 100644
index 00000000..6e69d176
--- /dev/null
+++ 
b/karavan-app/src/main/webui/src/ui/command-palette/useCommandPaletteStore.ts
@@ -0,0 +1,104 @@
+/*
+ * 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 {createWithEqualityFn} from "zustand/traditional";
+import {shallow} from "zustand/shallow";
+import {DslMetaModel} from "@designer/utils/DslMetaModel";
+
+interface CommandPaletteState {
+    fileName: string;
+    showPalette: boolean;
+    showProperties: boolean;
+    showSteps: boolean;
+    parentDsl?: string;
+    parentId: string;
+    selectorTabIndex?: string | number
+    selectedPosition?: number;
+    routeId?: string;
+    filter: string;
+    isRouteTemplate?: boolean;
+    selectedDsl?: DslMetaModel;
+    elements: DslMetaModel[];
+
+    setFileName: (fileName: string) => void;
+    setShowPalette: (showPalette: boolean) => void;
+    setShowProperties: (showProperties: boolean) => void;
+    setSelectedDsl: (selectedDsl?: DslMetaModel) => void;
+    setShowSteps: (showSteps: boolean) => void;
+    setParentDsl: (parentDsl?: string) => void;
+    setParentId: (parentId: string) => void;
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => void;
+    setSelectedPosition: (selectedPosition?: number) => void;
+    setRouteId: (routeId: string) => void;
+    setIsRouteTemplate: (isRouteTemplate: boolean) => void;
+    setFilter: (filter: string) => void;
+    setElements: (elements: DslMetaModel[]) => void;
+}
+
+export const useCommandPaletteStore = 
createWithEqualityFn<CommandPaletteState>((set) => ({
+    showPalette: false,
+    showProperties: false,
+    deleteMessage: '',
+    parentId: '',
+    showSteps: true,
+    isRouteTemplate: false,
+    filter: '',
+    elements: [],
+    fileName: undefined,
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => {
+        set({selectorTabIndex: selectorTabIndex})
+    },
+    setParentDsl: (parentDsl?: string) => {
+        set({parentDsl: parentDsl})
+    },
+    setSelectedDsl: (selectedDsl?: DslMetaModel) => {
+        set(state => ({
+            selectedDsl: selectedDsl,
+            showProperties: selectedDsl ? state.showProperties : false
+        }))
+    },
+    setShowPalette: (showPalette: boolean) => {
+        set({showPalette: showPalette})
+    },
+    setShowProperties: (showProperties: boolean) => {
+        set({showProperties: showProperties})
+    },
+    setShowSteps: (showSteps: boolean) => {
+        set({showSteps: showSteps})
+    },
+    setParentId: (parentId: string) => {
+        set({parentId: parentId})
+    },
+    setSelectedPosition: (selectedPosition?: number) => {
+        set({selectedPosition: selectedPosition})
+    },
+    setRouteId: (routeId: string) => {
+        set({routeId: routeId})
+    },
+    setIsRouteTemplate: (isRouteTemplate: boolean) => {
+        set({isRouteTemplate: isRouteTemplate})
+    },
+    setFilter: (filter: string) => {
+        set({ filter: filter })
+    },
+    setElements: (elements: DslMetaModel[])  => {
+        set({ elements: elements })
+    },
+    setFileName: (fileName: string) => {
+        set({ fileName: fileName })
+    },
+}), shallow)

Reply via email to