Repository: bluzky/salad_ui
Pull request #181: feat: add command component
Author: login: ilyabayel
Created: 2025-04-18T22:20:18Z | Merged: 2025-04-22T13:31:22Z

## Summary by Sourcery

Add a comprehensive command palette component to the SaladUI library, providing a flexible and interactive UI element for searching and selecting commands

New Features:
- Implement a full-featured command palette component with search, filtering, and keyboard navigation capabilities
- Create a command dialog wrapper for embedding command palettes in modal dialogs

Enhancements:
- Improve SVG icon rendering with more precise attributes
- Add Tailwind CSS form and typography plugins to the project

Tests:
- Create comprehensive test suite for all command palette component variations and interactions

Chores:
- Update package configuration to support modern JavaScript module system


Diff:
diff --git a/assets/components/command.js b/assets/components/command.js
new file mode 100644
index 0000000..95cb5fd
--- /dev/null
+++ b/assets/components/command.js
@@ -0,0 +1,129 @@
+import Component from "../core/component";
+import SaladUI from "..";
+
+/**
+ * CommandComponent for SaladUI
+ * Implements filtering, keyboard navigation, and selection for a command palette/list.
+ */
+class CommandComponent extends Component {
+  constructor(el, hookContext) {
+    super(el, { hookContext, ignoreItems: false });
+
+    // Set default field state
+    this.currentItemIdx = 0;
+
+    // Core elements
+    this.input = this.getPart("input");
+    this.list = this.getPart("list");
+    this.empty = this.getPart("empty");
+    this.groups = this.getAllParts("group");
+    this.items = this.getAllParts("item");
+
+    // Bind event handlers
+    this.input.addEventListener("input", this.handleSearch);
+
+    // Initial search/filter
+    this.handleSearch();
+
+    // Prevent default for navigation keys
+    this.config.preventDefaultKeys = ["Escape", "ArrowDown", "ArrowUp"];
+  }
+
+  getComponentConfig() {
+    return {
+      stateMachine: {
+        idle: { transitions: {} },
+      },
+      events: {
+        idle: {
+          keyMap: {
+            Enter: "selectItem",
+            ArrowDown: "focusNextItem",
+            ArrowUp: "focusPrevItem",
+            Escape: "blurInput",
+          },
+        },
+      },
+    };
+  }
+
+  // Focus item by index, wrap around if needed
+  focusItem(index) {
+    if (!this.selectableItems?.length) return;
+
+    // Wrap index
+    if (index < 0) index = this.selectableItems.length - 1;
+    if (index >= this.selectableItems.length) index = 0;
+
+    this.currentItemIdx = index;
+
+    // Deselect all
+    this.items.forEach((item) => {
+      item.setAttribute("data-selected", "false");
+      item.setAttribute("aria-selected", "false");
+    });
+
+    // Select current
+    const selectedItem = this.selectableItems[index];
+    selectedItem.setAttribute("data-selected", "true");
+    selectedItem.setAttribute("aria-selected", "true");
+  }
+
+  focusNextItem = () => this.focusItem(this.currentItemIdx + 1);
+  focusPrevItem = () => this.focusItem(this.currentItemIdx - 1);
+
+  blurInput = () => this.input?.blur();
+
+  selectItem = () => {
+    if (this.currentItemIdx === -1) return;
+    const item = this.selectableItems[this.currentItemIdx];
+    item.click();
+  };
+
+  // Handle search/filtering
+  handleSearch = () => {
+    const query = this.input.value.trim().toLowerCase();
+
+    // Filter items
+    this.items.forEach((item) => {
+      const text = item.textContent.trim().toLowerCase();
+      const visible = query === "" || text.includes(query);
+      item.setAttribute("data-visible", visible ? "true" : "false");
+    });
+
+    this.visibleItems = this.items.filter(
+      (el) => el.getAttribute("data-visible") === "true",
+    );
+
+    this.selectableItems = this.visibleItems.filter(
+      (el) => !el.hasAttribute("disabled"),
+    );
+
+    this.groups.forEach((group) => {
+      const visibleOptions = group.querySelectorAll("[data-visible='true']");
+      group.setAttribute(
+        "data-visible",
+        visibleOptions.length > 0 ? "true" : "false",
+      );
+    });
+
+    this.focusItem(0);
+
+    const noItems = this.visibleItems.length === 0;
+
+    if (noItems) {
+      this.empty.setAttribute("data-visible", "true");
+    } else {
+      this.empty.setAttribute("data-visible", "false");
+    }
+  };
+
+  beforeDestroy() {
+    this.input.removeEventListener("input", this.handleSearch);
+  }
+}
+
+// Register the component
+SaladUI.register("command", CommandComponent);
+
+export default CommandComponent;
diff --git a/assets/package-lock.json b/assets/package-lock.json
index 03fbf65..eb71a31 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -1,10 +1,15 @@
 {
-  "name": "assets",
+  "name": "salad_ui",
+  "version": "1.0.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
+      "name": "salad_ui",
+      "version": "1.0.0",
       "devDependencies": {
+        "@tailwindcss/forms": "^0.5.10",
+        "@tailwindcss/typography": "^0.5.16",
         "tailwindcss-animate": "^1.0.7"
       }
     },
@@ -141,6 +146,49 @@
         "node": ">=14"
       }
     },
+    "node_modules/@tailwindcss/forms": {
+      "version": "0.5.10",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
+      "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mini-svg-data-uri": "^1.2.3"
+      },
+      "peerDependencies": {
+        "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1"
+      }
+    },
+    "node_modules/@tailwindcss/typography": {
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
+      "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lodash.castarray": "^4.4.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "postcss-selector-parser": "6.0.10"
+      },
+      "peerDependencies": {
+        "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
+      }
+    },
+    "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
+      "version": "6.0.10",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+      "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/ansi-regex": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
@@ -317,10 +365,11 @@
       }
     },
     "node_modules/cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
       "dev": true,
+      "license": "MIT",
       "peer": true,
       "dependencies": {
         "path-key": "^3.1.0",
@@ -336,7 +385,6 @@
       "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
       "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
       "dev": true,
-      "peer": true,
       "bin": {
         "cssesc": "bin/cssesc"
       },
@@ -638,6 +686,27 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/lodash.castarray": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+      "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/lru-cache": {
       "version": "10.2.2",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
@@ -659,10 +728,11 @@
       }
     },
     "node_modules/micromatch": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
-      "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
       "dev": true,
+      "license": "MIT",
       "peer": true,
       "dependencies": {
         "braces": "^3.0.3",
@@ -672,6 +742,16 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/mini-svg-data-uri": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+      "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mini-svg-data-uri": "cli.js"
+      }
+    },
     "node_modules/minimatch": {
       "version": "9.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
@@ -711,9 +791,9 @@
       }
     },
     "node_modules/nanoid": {
-      "version": "3.3.7",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
-      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+      "version": "3.3.11",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
       "dev": true,
       "funding": [
         {
@@ -721,6 +801,7 @@
           "url": "https://github.com/sponsors/ai"
         }
       ],
+      "license": "MIT",
       "peer": true,
       "bin": {
         "nanoid": "bin/nanoid.cjs"
@@ -1367,8 +1448,7 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true,
-      "peer": true
+      "dev": true
     },
     "node_modules/which": {
       "version": "2.0.2",
@@ -1602,6 +1682,39 @@
       "optional": true,
       "peer": true
     },
+    "@tailwindcss/forms": {
+      "version": "0.5.10",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
+      "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==",
+      "dev": true,
+      "requires": {
+        "mini-svg-data-uri": "^1.2.3"
+      }
+    },
+    "@tailwindcss/typography": {
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
+      "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
+      "dev": true,
+      "requires": {
+        "lodash.castarray": "^4.4.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "postcss-selector-parser": "6.0.10"
+      },
+      "dependencies": {
+        "postcss-selector-parser": {
+          "version": "6.0.10",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+          "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+          "dev": true,
+          "requires": {
+            "cssesc": "^3.0.0",
+            "util-deprecate": "^1.0.2"
+          }
+        }
+      }
+    },
     "ansi-regex": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
@@ -1736,9 +1849,9 @@
       "peer": true
     },
     "cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
       "dev": true,
       "peer": true,
       "requires": {
@@ -1751,8 +1864,7 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
       "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true,
-      "peer": true
+      "dev": true
     },
     "didyoumean": {
       "version": "1.2.2",
@@ -1978,6 +2090,24 @@
       "dev": true,
       "peer": true
     },
+    "lodash.castarray": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+      "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
+      "dev": true
+    },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "dev": true
+    },
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
     "lru-cache": {
       "version": "10.2.2",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
@@ -1993,9 +2123,9 @@
       "peer": true
     },
     "micromatch": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
-      "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
       "dev": true,
       "peer": true,
       "requires": {
@@ -2003,6 +2133,12 @@
         "picomatch": "^2.3.1"
       }
     },
+    "mini-svg-data-uri": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+      "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
+      "dev": true
+    },
     "minimatch": {
       "version": "9.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
@@ -2033,9 +2169,9 @@
       }
     },
     "nanoid": {
-      "version": "3.3.7",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
-      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+      "version": "3.3.11",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
       "dev": true,
       "peer": true
     },
@@ -2463,8 +2599,7 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true,
-      "peer": true
+      "dev": true
     },
     "which": {
       "version": "2.0.2",
diff --git a/assets/package.json b/assets/package.json
index 266b195..617baf4 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -1,5 +1,10 @@
 {
+  "name": "salad_ui",
+  "version": "1.0.0",
+  "type": "module",
   "devDependencies": {
-    "tailwindcss-animate": "^1.0.7"
+    "tailwindcss-animate": "^1.0.7",
+    "@tailwindcss/forms": "^0.5.10",
+    "@tailwindcss/typography": "^0.5.16"
   }
 }
diff --git a/assets/tailwind.config.json b/assets/tailwind.config.js
similarity index 73%
rename from assets/tailwind.config.json
rename to assets/tailwind.config.js
index 59274e1..ac9daaa 100644
--- a/assets/tailwind.config.json
+++ b/assets/tailwind.config.js
@@ -1,11 +1,9 @@
 // See the Tailwind configuration guide for advanced usage
 // https://tailwindcss.com/docs/configuration
 
-module.exports = {
-  content: [
-    "../../../config/*.*exs",
-    "../lib/**/*.ex"
-  ],
+/** @type {import('tailwindcss').Config} */
+export default {
+  content: ["../../../config/*.*exs", "../lib/**/*.ex"],
   theme: {
     extend: {
       colors: require("./tailwind.colors.json"),
diff --git a/lib/salad_ui/command.ex b/lib/salad_ui/command.ex
new file mode 100644
index 0000000..c7bf292
--- /dev/null
+++ b/lib/salad_ui/command.ex
@@ -0,0 +1,326 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="empty"
+      class={classes(["py-6 text-center text-sm data-[visible=false]:hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a group of command items with a heading.
+
+  ## Attributes
+
+    * `:heading` (required) - The group heading.
+
+  ## Slots
+
+    * `:inner_block` (required) - The command items in the group.
+
+  ## Example
+
+      <.command_group heading="Settings">
+        <.command_item>Profile</.command_item>
+      </.command_group>
+  """
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <div
+      role="presentation"
+      data-part="group"
+      class="overflow-hidden p-1 text-foreground data-[visible=false]:hidden"
+    >
+      <div class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
+        {@heading}
+      </div>
+      <div role="group" class="list-none">
+        {render_slot(@inner_block)}
+      </div>
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a single command item (button).
+
+  ## Attributes
+
+    * `:disabled` - Whether the item is disabled.
+    * `:selected` - Whether the item is selected.
+    * All global attributes are passed to the `<button>` element.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command item.
+
+  ## Example
+
+      <.command_item phx-click="select_command">
+        <.icon name="calendar" />
+        <span>Calendar</span>
+      </.command_item>
+  """
+  attr :disabled, :boolean, default: false
+  attr :selected, :boolean, default: false
+  attr :rest, :global
+  slot :inner_block, required: true
+
+  def command_item(assigns) do
+    ~H"""
+    <button
+      tabindex="-1"
+      role="option"
+      data-part="item"
+      class="[&_svg]:h-4 [&_svg]:w-4 relative flex cursor-default w-full gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none disabled:pointer-events-none hover:bg-accent/75 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[visible=false]:hidden disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0"
+      data-selected={@selected}
+      aria-selected={@selected}
+      disabled={@disabled}
+      {@rest}
+    >
+      {render_slot(@inner_block)}
+    </button>
+    """
+  end
+
+  @doc """
+  Renders a keyboard shortcut hint for a command item.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The shortcut text.
+
+  ## Example
+
+      <.command_shortcut>⌘P</.command_shortcut>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_shortcut(assigns) do
+    ~H"""
+    <span
+      data-part="shortcut"
+      class={classes(["ml-auto text-xs tracking-widest text-muted-foreground", @class])}
+    >
+      {render_slot(@inner_block)}
+    </span>
+    """
+  end
+end
diff --git a/lib/salad_ui/dialog.ex b/lib/salad_ui/dialog.ex
index dd40d5e..019bd22 100644
--- a/lib/salad_ui/dialog.ex
+++ b/lib/salad_ui/dialog.ex
@@ -137,11 +137,15 @@ defmodule SaladUI.Dialog do
         >
           <svg
             xmlns="http://www.w3.org/2000/svg"
-            fill="none"
+            width="24"
+            height="24"
             viewBox="0 0 24 24"
-            stroke-width="1.5"
+            fill="none"
             stroke="currentColor"
-            class="w-5 h-5"
+            stroke-width="2"
+            stroke-linecap="round"
+            stroke-linejoin="round"
+            class="lucide lucide-x h-4 w-4"
           >
             <path d="M18 6 6 18"></path>
             <path d="m6 6 12 12"></path>
diff --git a/test/salad_ui/command_test.exs b/test/salad_ui/command_test.exs
new file mode 100644
index 0000000..783bc38
--- /dev/null
+++ b/test/salad_ui/command_test.exs
@@ -0,0 +1,190 @@
+defmodule SaladUI.CommandTest do
+  use ComponentCase
+
+  import SaladUI.Command
+
+  describe "command/1" do
+    test "renders the root command container with id and class" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command id="my-command" class="custom-class">
+          <span>Content</span>
+        </.command>
+        """)
+
+      assert html =~ ~s(id="my-command")
+      assert html =~ "custom-class"
+      assert html =~ "data-component=\"command\""
+      assert html =~ "<span>Content</span>"
+    end
+  end
+
+  describe "command_dialog/1" do
+    test "renders a dialog with command inside" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_dialog id="cmd-dialog" open={true}>
+          <.command_input placeholder="Type..." />
+        </.command_dialog>
+        """)
+
+      assert html =~ "cmd-dialog_dialog"
+      assert html =~ "data-component=\"command\""
+      assert html =~ "placeholder=\"Type...\""
+    end
+  end
+
+  describe "command_input/1" do
+    test "renders input with class and placeholder" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_input class="input-class" placeholder="Search..." />
+        """)
+
+      assert html =~ "input-class"
+      assert html =~ "placeholder=\"Search...\""
+      assert html =~ "data-part=\"command-input\""
+      assert html =~ "<input"
+    end
+  end
+
+  describe "command_list/1" do
+    test "renders the command list container" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_list class="list-class">
+          <span>List Content</span>
+        </.command_list>
+        """)
+
+      assert html =~ "data-part=\"command-list\""
+      assert html =~ "list-class"
+      assert html =~ "List Content"
+    end
+  end
+
+  describe "command_empty/1" do
+    test "renders the empty message" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_empty class="empty-class">
+          <span>No results</span>
+        </.command_empty>
+        """)
+
+      assert html =~ "data-part=\"command-empty\""
+      assert html =~ "empty-class"
+      assert html =~ "No results"
+    end
+  end
+
+  describe "command_group/1" do
+    test "renders a group with heading and items" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_group heading="Group Heading">
+          <.command_item>Item 1</.command_item>
+        </.command_group>
+        """)
+
+      assert html =~ "Group Heading"
+      assert html =~ "role=\"group\""
+      assert html =~ "Item 1"
+    end
+  end
+
+  describe "command_item/1" do
+    test "renders a command item button" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_item>
+          <span>Item</span>
+        </.command_item>
+        """)
+
+      assert html =~ "<button"
+      assert html =~ "data-part=\"command-item\""
+      assert html =~ "<span>Item</span>"
+    end
+
+    test "renders with disabled and selected attributes" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_item disabled={true} selected={true}>
+          <span>Item</span>
+        </.command_item>
+        """)
+
+      assert html =~ " disabled>"
+      assert html =~ " data-selected "
+      assert html =~ " aria-selected "
+    end
+  end
+
+  describe "command_shortcut/1" do
+    test "renders a shortcut span" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command_shortcut class="shortcut-class">⌘P</.command_shortcut>
+        """)
+
+      assert html =~ "data-part=\"command-shortcut\""
+      assert html =~ "shortcut-class"
+      assert html =~ "⌘P"
+    end
+  end
+
+  describe "integration: full command palette" do
+    test "renders a complete command palette structure" do
+      assigns = %{}
+
+      html =
+        rendered_to_string(~H"""
+        <.command id="cmd">
+          <.command_input placeholder="Type a command..." />
+          <.command_empty>No results found</.command_empty>
+          <.command_list>
+            <.command_group heading="Actions">
+              <.command_item>
+                <span>Action 1</span>
+                <.command_shortcut>⌘A</.command_shortcut>
+              </.command_item>
+              <.command_item disabled>
+                <span>Action 2</span>
+                <.command_shortcut>⌘B</.command_shortcut>
+              </.command_item>
+            </.command_group>
+          </.command_list>
+        </.command>
+        """)
+
+      assert html =~ "data-component=\"command\""
+      assert html =~ "placeholder=\"Type a command...\""
+      assert html =~ "No results found"
+      assert html =~ "Actions"
+      assert html =~ "Action 1"
+      assert html =~ "⌘A"
+      assert html =~ "Action 2"
+      assert html =~ "⌘B"
+      assert html =~ " disabled>"
+    end
+  end
+end


Reviews:
user: login: sourcery-ai[bot]
body: Hey @ilyabayel - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider adding a state machine to the LiveView to manage the command component's state.
- The javascript component could use some comments to explain what the methods do.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟡 **General issues**: 1 issue found
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟢 **Complexity**: all looks good
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-18T22:20:50Z
user: login: sourcery-ai[bot]
body: Hey @ilyabayel - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider using `Phoenix.Component.focus_wrap/1` to handle keyboard navigation and focus management.
- The javascript component could be simplified by leveraging `aria-activedescendant` for managing focus instead of manually setting `data-selected`.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟡 **General issues**: 1 issue found
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟡 **Complexity**: 1 issue found
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-20T05:12:51Z
user: login: sourcery-ai[bot]
body: Hey @ilyabayel - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider using `Phoenix.Component.focus_wrap/1` to handle keyboard navigation and focus management within the command palette.
- The JavaScript component could benefit from debouncing the `handleSearch` function to improve performance with rapid input.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟢 **General issues**: all looks good
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟢 **Complexity**: all looks good
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-20T11:46:28Z
user: login: sourcery-ai[bot]
body: Hey @ilyabayel - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider adding a live demo or interactive example to the documentation to showcase the command palette's functionality.
- The javascript component could use some comments to explain the logic.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟡 **General issues**: 1 issue found
- 🟢 **Security**: all looks good
- 🟡 **Testing**: 1 issue found
- 🟡 **Complexity**: 1 issue found
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-21T15:42:27Z
user: login: bluzky
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-21T16:00:39Z
user: login: ilyabayel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T17:35:55Z
user: login: ilyabayel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T17:37:40Z
user: login: ilyabayel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T18:52:54Z
user: login: ilyabayel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T11:34:16Z
user: login: ilyabayel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T11:34:19Z
user: login: ilyabayel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T11:34:23Z
user: login: bluzky
state: APPROVED
author_association: OWNER
submitted_at: 2025-04-22T13:31:14Z

Review comments:
diff_hunk: @@ -0,0 +1,102 @@
+defmodule SaladUI.Command do
+  @moduledoc false
+  use SaladUI, :component
+
+  import SaladUI.Icon
+
+  attr :id, :string, required: true
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      phx-hook="SaladUI"
+      class="relative bg-white rounded-lg border border-gray-200 shadow-md overflow-hidden focus-within:ring-2 focus-within:ring-blue-500 min-w-full md:min-w-[340px] lg:min-w-[480px]"
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  def command_input(assigns) do
+    ~H"""
+    <div
+      data-part="command-input-wrapper"
+      class="flex items-center px-3 py-1 border-b border-gray-200"
+    >
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        placeholder="Type a command or search..."
+        autocomplete="off"
+        class="flex-1 text-sm bg-transparent outline-none placeholder-gray-400 text-gray-900 border-none focus:ring-transparent"
+      />
+    </div>
+    """
+  end
+
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <ul data-part="command-list" tabindex="-1" class="max-h-60 overflow-y-auto">
+      {render_slot(@inner_block)}
+    </ul>
+    """
+  end
+
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <li>
+      <div class="px-4 py-2 text-xs text-gray-500">
+        {@heading}
+      </div>
+      <ul>
+        {render_slot(@inner_block)}
+      </ul>
+    </li>
+    """
+  end
+
+  attr :disabled, :boolean, default: false
+  attr :value, :string, required: true
+  attr :rest, :global
+  slot :inner_block, required: true
+
+  def command_item(assigns) do
+    dbg(assigns)
path: lib/salad_ui/command.ex
user: login: sourcery-ai[bot]
body: **issue (bug_risk):** Debugging statement left in production code.

The dbg(assigns) call in the command_item function should be removed unless it is intentionally left for verbose logging in production.
created_at: 2025-04-18T22:20:50Z
author_association: NONE
diff_hunk: @@ -34,9 +34,10 @@ class Component {
     this.allParts = Array.from(this.el.querySelectorAll("[data-part]")).concat([
       this.el,
     ]);
+
     if (ignoreItems) {
       this.allParts = this.allParts.filter(
-        (element) => !element.dataset.part.startsWith("item"),
+        (element) => !element.dataset?.part?.startsWith("item"),
       );
path: assets/core/component.js
user: login: sourcery-ai[bot]
body: **suggestion (bug_risk):** Using optional chaining adds robustness when dataset or dataset.part is missing.

This change ensures that if for some reason the data attribute is not set, the code won’t throw an error. Verify that this relaxation in checking is acceptable in all expected contexts.
created_at: 2025-04-20T05:12:50Z
author_association: NONE
diff_hunk: @@ -0,0 +1,134 @@
+import Component from "../core/component";
+import SaladUI from "..";
+
+/**
+ * CommandComponent for SaladUI
+ * Implements filtering, keyboard navigation, and selection for a command palette/list.
+ */
+class CommandComponent extends Component {
+  constructor(el, hookContext) {
+    super(el, { hookContext });
+
+    // Set default field state
+    this.currentItemIdx = 0;
+
+    // Core elements
+    this.input = this.getPart("command-input");
+    this.list = this.getPart("command-list");
+    this.empty = this.getPart("command-empty");
+    this.groups = this.getAllParts("command-group");
+    this.items = this.getAllParts("command-item");
+
+    // Bind event handlers
+    this.input.addEventListener("input", this.handleSearch);
path: assets/components/command.js
user: login: sourcery-ai[bot]
body: **issue (complexity):** Consider extracting filtering and selection logic into a separate utility module to improve the component's focus and testability of these functions separately, which reduces complexity and cognitive load in the component.

Consider extracting the filtering and selection logic into their own utilities to reduce cognitive load in the component. For example, you can create a helper module to encapsulate the filtering, group visibility, and selectable items logic. This also allows you to unit test these functions separately.

**Step 1. Create a helper module:**

```js
// utils/commandUtils.js

export function filterItems(items, query) {
  const filtered = items.map((item) => {
    const text = item.textContent.trim().toLowerCase();
    const visible = query === "" || text.includes(query);
    item.setAttribute("data-visible", visible ? "true" : "false");
    return item;
  });
  return filtered;
}

export function updateGroupVisibility(groups) {
  groups.forEach((group) => {
    const visibleOptions = group.querySelectorAll("[data-visible='true']");
    group.setAttribute("data-visible", visibleOptions.length > 0 ? "true" : "false");
  });
}

export function getSelectableItems(items) {
  const visibleItems = items.filter((item) => item.getAttribute("data-visible") === "true");
  return visibleItems.filter((item) => !item.hasAttribute("disabled"));
}
```

**Step 2. Update the component to use these functions:**

```js
import { filterItems, updateGroupVisibility, getSelectableItems } from "./utils/commandUtils";

handleSearch = () => {
  const query = this.input.value.trim().toLowerCase();

  // Perform filtering
  filterItems(this.items, query);

  // Update groups and selectable items
  updateGroupVisibility(this.groups);
  this.selectableItems = getSelectableItems(this.items);

  // Reset focus to first selectable item
  this.focusItem(0);

  // Toggle empty state based on visible items
  const isSomeItemVisible = this.items.some(
    (item) => item.getAttribute("data-visible") === "true",
  );
  this.empty.setAttribute("data-visible", isSomeItemVisible ? "false" : "true");
};
```

This approach keeps the component focused on orchestrating behavior while offloading filtering and DOM update logic.
created_at: 2025-04-20T05:12:50Z
author_association: NONE
diff_hunk: @@ -0,0 +1,129 @@
+import Component from "../core/component";
+import SaladUI from "..";
+
+/**
+ * CommandComponent for SaladUI
+ * Implements filtering, keyboard navigation, and selection for a command palette/list.
+ */
+class CommandComponent extends Component {
+  constructor(el, hookContext) {
+    super(el, { hookContext });
+
+    // Set default field state
+    this.currentItemIdx = 0;
+
+    // Core elements
+    this.input = this.getPart("command-input");
+    this.list = this.getPart("command-list");
+    this.empty = this.getPart("command-empty");
+    this.groups = this.getAllParts("command-group");
+    this.items = this.getAllParts("command-item");
+
+    // Bind event handlers
+    this.input.addEventListener("input", this.handleSearch);
path: assets/components/command.js
user: login: sourcery-ai[bot]
body: **suggestion (performance):** Consider debouncing the search handler to optimize performance.

Since the handleSearch method executes on each input event, if the command list grows large or if users type quickly, debouncing the handler may improve responsiveness and reduce unnecessary DOM reads.
created_at: 2025-04-21T15:42:26Z
author_association: NONE
diff_hunk: @@ -0,0 +1,190 @@
+defmodule SaladUI.CommandTest do
path: test/salad_ui/command_test.exs
user: login: sourcery-ai[bot]
body: **issue (testing):** Missing tests for keyboard navigation

The component description mentions keyboard navigation, but the test file doesn't seem to include tests for arrow keys, enter, or escape key functionalities.  It's crucial to verify these interactions to ensure the component behaves as expected.
created_at: 2025-04-21T15:42:26Z
author_association: NONE
diff_hunk: @@ -0,0 +1,129 @@
+import Component from "../core/component";
+import SaladUI from "..";
+
+/**
+ * CommandComponent for SaladUI
+ * Implements filtering, keyboard navigation, and selection for a command palette/list.
+ */
+class CommandComponent extends Component {
+  constructor(el, hookContext) {
+    super(el, { hookContext });
+
+    // Set default field state
+    this.currentItemIdx = 0;
+
+    // Core elements
+    this.input = this.getPart("command-input");
+    this.list = this.getPart("command-list");
+    this.empty = this.getPart("command-empty");
+    this.groups = this.getAllParts("command-group");
+    this.items = this.getAllParts("command-item");
+
+    // Bind event handlers
+    this.input.addEventListener("input", this.handleSearch);
+
+    // Initial search/filter
+    this.handleSearch();
+
+    // Prevent default for navigation keys
+    this.config.preventDefaultKeys = ["Escape", "ArrowDown", "ArrowUp"];
+  }
+
+  getComponentConfig() {
+    return {
+      stateMachine: {
+        idle: { transitions: {} },
+      },
+      events: {
+        idle: {
+          keyMap: {
+            Enter: "selectItem",
+            ArrowDown: "focusNextItem",
+            ArrowUp: "focusPrevItem",
+            Escape: "blurInput",
+          },
+        },
+      },
+    };
+  }
+
+  // Focus item by index, wrap around if needed
+  focusItem(index) {
+    if (!this.selectableItems?.length) return;
+
+    // Wrap index
+    if (index < 0) index = this.selectableItems.length - 1;
+    if (index >= this.selectableItems.length) index = 0;
+
+    this.currentItemIdx = index;
+
+    // Deselect all
+    this.items.forEach((item) => {
path: assets/components/command.js
user: login: sourcery-ai[bot]
body: **issue (complexity):** Consider refactoring the filtering logic in `handleSearch` to combine DOM updates and filtering into a single loop to reduce iterations over the items array.

Consider refactoring the filtering logic within `handleSearch` to traverse the items only once. For example, instead of sequentially updating DOM attributes and filtering items in separate passes, combine them into a single loop:

```js
handleSearch = () => {
  const query = this.input.value.trim().toLowerCase();
  const visibleItems = [];
  const selectableItems = [];

  this.items.forEach((item) => {
    const text = item.textContent.trim().toLowerCase();
    const visible = query === "" || text.includes(query);
    item.setAttribute("data-visible", visible ? "true" : "false");

    if (visible) {
      visibleItems.push(item);
      if (!item.hasAttribute("disabled")) {
        selectableItems.push(item);
      }
    }
  });

  this.visibleItems = visibleItems;
  this.selectableItems = selectableItems;

  this.groups.forEach((group) => {
    const visibleOptions = group.querySelectorAll("[data-visible='true']");
    group.setAttribute("data-visible", visibleOptions.length > 0 ? "true" : "false");
  });

  this.focusItem(0);
  this.empty.setAttribute("data-visible", visibleItems.length === 0 ? "true" : "false");
};
```

This consolidation should reduce iterations over the items while preserving existing functionality.
created_at: 2025-04-21T15:42:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
path: lib/salad_ui/command.ex
user: login: bluzky
body: this is just example right? you can setup a new page with salad_storybook for testing the result
created_at: 2025-04-21T15:48:56Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
path: lib/salad_ui/command.ex
user: login: bluzky
body: we use part name `data-part="list"` for consistency because command only contains its parts
created_at: 2025-04-21T15:51:13Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="command-empty"
path: lib/salad_ui/command.ex
user: login: bluzky
body: ```suggestion
      data-part="empty"
```
created_at: 2025-04-21T15:51:29Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="command-empty"
+      class={classes(["py-6 text-center text-sm data-[visible=false]:hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a group of command items with a heading.
+
+  ## Attributes
+
+    * `:heading` (required) - The group heading.
+
+  ## Slots
+
+    * `:inner_block` (required) - The command items in the group.
+
+  ## Example
+
+      <.command_group heading="Settings">
+        <.command_item>Profile</.command_item>
+      </.command_group>
+  """
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <div
+      role="presentation"
+      data-part="command-group"
path: lib/salad_ui/command.ex
user: login: bluzky
body: ```suggestion
      data-part="group"
```
created_at: 2025-04-21T15:51:40Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="command-empty"
+      class={classes(["py-6 text-center text-sm data-[visible=false]:hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a group of command items with a heading.
+
+  ## Attributes
+
+    * `:heading` (required) - The group heading.
+
+  ## Slots
+
+    * `:inner_block` (required) - The command items in the group.
+
+  ## Example
+
+      <.command_group heading="Settings">
+        <.command_item>Profile</.command_item>
+      </.command_group>
+  """
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <div
+      role="presentation"
+      data-part="command-group"
+      class="overflow-hidden p-1 text-foreground data-[visible=false]:hidden"
+    >
+      <div class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
+        {@heading}
+      </div>
+      <div role="group" class="list-none">
+        {render_slot(@inner_block)}
+      </div>
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a single command item (button).
+
+  ## Attributes
+
+    * `:disabled` - Whether the item is disabled.
+    * `:selected` - Whether the item is selected.
+    * All global attributes are passed to the `<button>` element.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command item.
+
+  ## Example
+
+      <.command_item phx-click="select_command">
+        <.icon name="calendar" />
+        <span>Calendar</span>
+      </.command_item>
+  """
+  attr :disabled, :boolean, default: false
+  attr :selected, :boolean, default: false
+  attr :rest, :global
+  slot :inner_block, required: true
+
+  def command_item(assigns) do
+    ~H"""
+    <button
+      tabindex="-1"
+      role="option"
+      data-part="command-item"
path: lib/salad_ui/command.ex
user: login: bluzky
body: ```suggestion
      data-part="item"
```
created_at: 2025-04-21T15:51:53Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="command-empty"
+      class={classes(["py-6 text-center text-sm data-[visible=false]:hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a group of command items with a heading.
+
+  ## Attributes
+
+    * `:heading` (required) - The group heading.
+
+  ## Slots
+
+    * `:inner_block` (required) - The command items in the group.
+
+  ## Example
+
+      <.command_group heading="Settings">
+        <.command_item>Profile</.command_item>
+      </.command_group>
+  """
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <div
+      role="presentation"
+      data-part="command-group"
+      class="overflow-hidden p-1 text-foreground data-[visible=false]:hidden"
+    >
+      <div class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
+        {@heading}
+      </div>
+      <div role="group" class="list-none">
+        {render_slot(@inner_block)}
+      </div>
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a single command item (button).
+
+  ## Attributes
+
+    * `:disabled` - Whether the item is disabled.
+    * `:selected` - Whether the item is selected.
+    * All global attributes are passed to the `<button>` element.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command item.
+
+  ## Example
+
+      <.command_item phx-click="select_command">
+        <.icon name="calendar" />
+        <span>Calendar</span>
+      </.command_item>
+  """
+  attr :disabled, :boolean, default: false
+  attr :selected, :boolean, default: false
+  attr :rest, :global
+  slot :inner_block, required: true
+
+  def command_item(assigns) do
+    ~H"""
+    <button
+      tabindex="-1"
+      role="option"
+      data-part="command-item"
+      class="[&_svg]:h-4 [&_svg]:w-4 relative flex cursor-default w-full gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none disabled:pointer-events-none hover:bg-accent/75 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[visible=false]:hidden disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0"
+      data-selected={@selected}
+      aria-selected={@selected}
+      disabled={@disabled}
+      {@rest}
+    >
+      {render_slot(@inner_block)}
+    </button>
+    """
+  end
+
+  @doc """
+  Renders a keyboard shortcut hint for a command item.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The shortcut text.
+
+  ## Example
+
+      <.command_shortcut>⌘P</.command_shortcut>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_shortcut(assigns) do
+    ~H"""
+    <span
+      data-part="command-shortcut"
path: lib/salad_ui/command.ex
user: login: bluzky
body: ```suggestion
      data-part="shortcut"
```
created_at: 2025-04-21T15:52:05Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
path: lib/salad_ui/command.ex
user: login: ilyabayel
body: Actually they really have `command_dialog` component, if you look at the examples here
https://ui.shadcn.com/docs/components/command

but probably it should not have dialog trigger
created_at: 2025-04-21T17:35:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
path: lib/salad_ui/command.ex
user: login: ilyabayel
body: good to know, I'll change it for every part 
created_at: 2025-04-21T17:37:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
path: lib/salad_ui/command.ex
user: login: ilyabayel
body: @bluzky here's MR to storybook https://github.com/bluzky/salad_storybook/pull/26/files
created_at: 2025-04-21T18:52:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="command-empty"
+      class={classes(["py-6 text-center text-sm data-[visible=false]:hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a group of command items with a heading.
+
+  ## Attributes
+
+    * `:heading` (required) - The group heading.
+
+  ## Slots
+
+    * `:inner_block` (required) - The command items in the group.
+
+  ## Example
+
+      <.command_group heading="Settings">
+        <.command_item>Profile</.command_item>
+      </.command_group>
+  """
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <div
+      role="presentation"
+      data-part="command-group"
path: lib/salad_ui/command.ex
user: login: ilyabayel
body: updated
created_at: 2025-04-22T11:34:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="command-empty"
+      class={classes(["py-6 text-center text-sm data-[visible=false]:hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a group of command items with a heading.
+
+  ## Attributes
+
+    * `:heading` (required) - The group heading.
+
+  ## Slots
+
+    * `:inner_block` (required) - The command items in the group.
+
+  ## Example
+
+      <.command_group heading="Settings">
+        <.command_item>Profile</.command_item>
+      </.command_group>
+  """
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <div
+      role="presentation"
+      data-part="command-group"
+      class="overflow-hidden p-1 text-foreground data-[visible=false]:hidden"
+    >
+      <div class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
+        {@heading}
+      </div>
+      <div role="group" class="list-none">
+        {render_slot(@inner_block)}
+      </div>
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a single command item (button).
+
+  ## Attributes
+
+    * `:disabled` - Whether the item is disabled.
+    * `:selected` - Whether the item is selected.
+    * All global attributes are passed to the `<button>` element.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command item.
+
+  ## Example
+
+      <.command_item phx-click="select_command">
+        <.icon name="calendar" />
+        <span>Calendar</span>
+      </.command_item>
+  """
+  attr :disabled, :boolean, default: false
+  attr :selected, :boolean, default: false
+  attr :rest, :global
+  slot :inner_block, required: true
+
+  def command_item(assigns) do
+    ~H"""
+    <button
+      tabindex="-1"
+      role="option"
+      data-part="command-item"
path: lib/salad_ui/command.ex
user: login: ilyabayel
body: updated
created_at: 2025-04-22T11:34:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,327 @@
+defmodule SaladUI.Command do
+  @moduledoc """
+  Command palette components for SaladUI.
+
+  Provides a set of components to build a command palette or searchable menu, similar to those found in modern applications.
+
+  ## Example
+
+      <.command id="command" class="rounded-lg border shadow-md md:min-w-[450px] w-full lg:max-w-[600px]">
+        <.command_input placeholder="Type a command or search..." />
+        <.command_empty>
+          <span>No results found</span>
+        </.command_empty>
+        <.command_list>
+          <.command_group heading="Suggestions">
+            <.command_item phx-value-name="calendar" phx-click="select_command">
+              <.calendar class="w-4 h-4"/>
+              <span>Calendar</span>
+            </.command_item>
+            ...
+          </.command_group>
+          <.command_group heading="Settings">
+            <.command_item phx-value-name="profile" phx-click="select_command">
+              <.user class="w-4 h-4"/>
+              <span>Profile</span>
+              <.command_shortcut>⌘P</.command_shortcut>
+            </.command_item>
+            ...
+          </.command_group>
+        </.command_list>
+      </.command>
+  """
+  use SaladUI, :component
+
+  import SaladUI.Dialog
+  import SaladUI.Icon
+
+  @doc """
+  Renders the root command palette container.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command id="my-command">
+        ...
+      </.command>
+  """
+  attr :id, :string, required: true
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command(assigns) do
+    ~H"""
+    <div
+      id={@id}
+      tabindex="-1"
+      data-component="command"
+      data-part="root"
+      phx-hook="SaladUI"
+      class={
+        classes([
+          "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
+          @class
+        ])
+      }
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a command palette inside a dialog.
+
+  ## Attributes
+
+    * `:id` (required) - The unique id for the command palette.
+    * `:open` - Whether the dialog is open.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command palette.
+
+  ## Example
+
+      <.command_dialog id="my-command" open={@show_command}>
+        ...
+      </.command_dialog>
+  """
+  attr :id, :string, required: true
+  attr :open, :boolean, default: false
+  slot :inner_block, required: true
+
+  def command_dialog(assigns) do
+    ~H"""
+    <.dialog id={@id <> "_dialog"} open={@open}>
+      <.dialog_trigger>Click me</.dialog_trigger>
+      <.dialog_content class="overflow-hidden p-0">
+        <.command id={@id} class="[&_[data-part='command-input']]:h-12">
+          {render_slot(@inner_block)}
+        </.command>
+      </.dialog_content>
+    </.dialog>
+    """
+  end
+
+  @doc """
+  Renders the input field for searching/filtering commands.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+    * All global attributes are passed to the `<input>` element.
+
+  ## Example
+
+      <.command_input placeholder="Type a command..." />
+  """
+  attr :class, :any, default: ""
+  attr :rest, :global, default: %{}
+
+  def command_input(assigns) do
+    ~H"""
+    <div data-part="command-input-wrapper" class="flex items-center border-b px-3">
+      <.icon name="hero-magnifying-glass" />
+      <input
+        type="text"
+        data-part="command-input"
+        autocomplete="off"
+        autocorrect="off"
+        autocapitalize="off"
+        spellcheck="false"
+        class={
+          classes([
+            "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-transparent",
+            @class
+          ])
+        }
+        {@rest}
+      />
+    </div>
+    """
+  end
+
+  @doc """
+  Renders the list container for command items and groups.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command list (groups/items).
+
+  ## Example
+
+      <.command_list>
+        <.command_group heading="Actions">
+          <.command_item>...</.command_item>
+        </.command_group>
+      </.command_list>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_list(assigns) do
+    ~H"""
+    <div
+      data-part="command-list"
+      tabindex="-1"
+      role="listbox"
+      class={classes(["max-h-[300px] overflow-y-auto overflow-x-hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a message when no command results are found.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content to display when empty.
+
+  ## Example
+
+      <.command_empty>
+        <span>No results found</span>
+      </.command_empty>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_empty(assigns) do
+    ~H"""
+    <div
+      data-visible="false"
+      data-part="command-empty"
+      class={classes(["py-6 text-center text-sm data-[visible=false]:hidden", @class])}
+    >
+      {render_slot(@inner_block)}
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a group of command items with a heading.
+
+  ## Attributes
+
+    * `:heading` (required) - The group heading.
+
+  ## Slots
+
+    * `:inner_block` (required) - The command items in the group.
+
+  ## Example
+
+      <.command_group heading="Settings">
+        <.command_item>Profile</.command_item>
+      </.command_group>
+  """
+  attr :heading, :string, required: true
+  slot :inner_block, required: true
+
+  def command_group(assigns) do
+    ~H"""
+    <div
+      role="presentation"
+      data-part="command-group"
+      class="overflow-hidden p-1 text-foreground data-[visible=false]:hidden"
+    >
+      <div class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
+        {@heading}
+      </div>
+      <div role="group" class="list-none">
+        {render_slot(@inner_block)}
+      </div>
+    </div>
+    """
+  end
+
+  @doc """
+  Renders a single command item (button).
+
+  ## Attributes
+
+    * `:disabled` - Whether the item is disabled.
+    * `:selected` - Whether the item is selected.
+    * All global attributes are passed to the `<button>` element.
+
+  ## Slots
+
+    * `:inner_block` (required) - The content of the command item.
+
+  ## Example
+
+      <.command_item phx-click="select_command">
+        <.icon name="calendar" />
+        <span>Calendar</span>
+      </.command_item>
+  """
+  attr :disabled, :boolean, default: false
+  attr :selected, :boolean, default: false
+  attr :rest, :global
+  slot :inner_block, required: true
+
+  def command_item(assigns) do
+    ~H"""
+    <button
+      tabindex="-1"
+      role="option"
+      data-part="command-item"
+      class="[&_svg]:h-4 [&_svg]:w-4 relative flex cursor-default w-full gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none disabled:pointer-events-none hover:bg-accent/75 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[visible=false]:hidden disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0"
+      data-selected={@selected}
+      aria-selected={@selected}
+      disabled={@disabled}
+      {@rest}
+    >
+      {render_slot(@inner_block)}
+    </button>
+    """
+  end
+
+  @doc """
+  Renders a keyboard shortcut hint for a command item.
+
+  ## Attributes
+
+    * `:class` - Additional classes to apply.
+
+  ## Slots
+
+    * `:inner_block` (required) - The shortcut text.
+
+  ## Example
+
+      <.command_shortcut>⌘P</.command_shortcut>
+  """
+  attr :class, :any, default: ""
+  slot :inner_block, required: true
+
+  def command_shortcut(assigns) do
+    ~H"""
+    <span
+      data-part="command-shortcut"
path: lib/salad_ui/command.ex
user: login: ilyabayel
body: updated
created_at: 2025-04-22T11:34:23Z
author_association: CONTRIBUTOR

PR comments:
user: login: sourcery-ai[bot]
created_at: 2025-04-18T22:20:22Z
body: <!-- Generated by sourcery-ai[bot]: start review_guide -->

## Reviewer's Guide by Sourcery

This pull request introduces a new command palette component to the SaladUI library, enhances SVG icon rendering in the dialog component, and updates the project's package configuration to support modern JavaScript module systems and include Tailwind CSS plugins. The command palette component provides a flexible and interactive way for users to execute commands or navigate through options.

#### Sequence diagram for selecting a command item

```mermaid
sequenceDiagram
    participant User
    participant CommandComponent
    participant CommandItem

    User->>CommandComponent: Types in command input
    CommandComponent->>CommandComponent: Filters command items
    User->>CommandItem: Clicks on CommandItem
    CommandItem->>CommandComponent: click()
    CommandComponent->>CommandComponent: selectItem()
    CommandComponent-->>User: Executes command
```

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Introduces a new command palette component with associated sub-components for input, lists, groups, items, and shortcuts. | <ul><li>Adds `command`, `command_dialog`, `command_input`, `command_list`, `command_empty`, `command_group`, `command_item`, and `command_shortcut` components.</li><li>Each component is implemented as a function component using `~H` sigil for rendering.</li><li>Includes attributes for customization and slots for content projection.</li><li>Adds corresponding tests for each component to ensure correct rendering and behavior.</li></ul> | `lib/salad_ui/command.ex`<br/>`test/salad_ui/command_test.exs`<br/>`assets/components/command.js` |
| Updates SVG icon rendering in the dialog component to use more precise attributes for size and stroke. | <ul><li>Adds `width` and `height` attributes to the `<svg>` element.</li><li>Sets `fill` to "none".</li><li>Adds `stroke-width`, `stroke-linecap`, and `stroke-linejoin` attributes for better stroke control.</li><li>Updates the `class` attribute to use `lucide lucide-x h-4 w-4` for styling.</li></ul> | `lib/salad_ui/dialog.ex` |
| Updates the project's package configuration to support modern JavaScript module systems and adds Tailwind CSS plugins. | <ul><li>Adds `name`, `version`, and `type` fields to `package.json`.</li><li>Adds `@tailwindcss/forms` and `@tailwindcss/typography` as dev dependencies.</li><li>Creates a `tailwind.config.js` file to configure Tailwind CSS.</li><li>Removes `package-lock.json`.</li></ul> | `assets/package.json`<br/>`assets/package-lock.json`<br/>`assets/tailwind.config.js` |

---

<details>
<summary>Tips and commands</summary>

#### Interacting with Sourcery

- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.
- **Continue discussions:** Reply directly to Sourcery's review comments.
- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an
  issue from a review comment by replying to it. You can also reply to a
  review comment with `@sourcery-ai issue` to create an issue from it.
- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull
  request title to generate a title at any time. You can also comment
  `@sourcery-ai title` on the pull request to (re-)generate the title at any time.
- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in
  the pull request body to generate a PR summary at any time exactly where you
  want it. You can also comment `@sourcery-ai summary` on the pull request to
  (re-)generate the summary at any time.
- **Generate reviewer's guide:** Comment `@sourcery-ai guide` on the pull
  request to (re-)generate the reviewer's guide at any time.
- **Resolve all Sourcery comments:** Comment `@sourcery-ai resolve` on the
  pull request to resolve all Sourcery comments. Useful if you've already
  addressed all the comments and don't want to see them anymore.
- **Dismiss all Sourcery reviews:** Comment `@sourcery-ai dismiss` on the pull
  request to dismiss all existing Sourcery reviews. Especially useful if you
  want to start fresh with a new review - don't forget to comment
  `@sourcery-ai review` to trigger a new review!
- **Generate a plan of action for an issue:** Comment `@sourcery-ai plan` on
  an issue to generate a plan of action for it.

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) to:
- Enable or disable review features such as the Sourcery-generated pull request
  summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.

#### Getting Help

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
author_association: NONE
user: login: ilyabayel
created_at: 2025-04-20T11:45:46Z
body: @sourcery-ai review
author_association: CONTRIBUTOR
user: login: ilyabayel
created_at: 2025-04-21T14:45:57Z
body: Hey, @bluzky. Could you please mark this MR as done and review it. 

I think it would be good to add playwright tests to add user interaction testing like keyboard navigation, click testing etc. But this better to be done later
author_association: CONTRIBUTOR
user: login: bluzky
created_at: 2025-04-21T15:42:41Z
body: Thanks @ilyabayel, could you help me to setup js test. I don't have much experience with it.

author_association: OWNER