{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "command", "type": "registry:block", "title": "Command", "description": "Provides fast searchable actions and navigation interfaces.", "author": "Wensity ", "dependencies": [ "@base-ui/react", "@tabler/icons-react", "clsx", "tailwind-merge" ], "registryDependencies": [], "files": [ { "path": "registry/wensity/lib/utils.ts", "type": "registry:lib", "target": "@lib/utils.ts", "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n" }, { "path": "registry/wensity/command.tsx", "type": "registry:component", "target": "@components/wensity/command.tsx", "content": "\"use client\";\n\n/**\n * Command — Base UI Autocomplete (inline) + Dialog foundation (Phase 5).\n *\n * The picker uses Base UI Autocomplete with `inline open mode=\"none\"` so it\n * renders as a flat filterable list — filtering, keyboard nav, and highlight\n * come from Base UI while the Wensity nested-page stack lives outside as\n * plain React state. Backspace on an empty query and Escape both pop the\n * submenu stack before closing.\n *\n * D-17: CommandDialog onOpenChange → (open: boolean) => void. Dialog.Trigger\n * uses asChild → render + nativeButton host match, matching Dialog.tsx.\n */\n\nimport * as React from \"react\";\nimport { Autocomplete } from \"@base-ui/react/autocomplete\";\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport {\n IconArrowLeft,\n IconChevronRight,\n IconCornerDownLeft,\n IconSearch,\n} from \"@tabler/icons-react\";\nimport { cn } from \"@/lib/utils\";\n\nconst commandDialogSurfaceClass = cn(\n \"border border-[var(--border)] bg-[var(--primitive-surface-elevated)] text-[var(--foreground)]\",\n \"[box-shadow:var(--primitive-shadow-modal)]\",\n \"isolate overflow-hidden rounded-[var(--primitive-radius-surface)]\",\n);\n\nconst backdropClass = cn(\n \"fixed inset-0 z-[var(--primitive-z-overlay)] bg-[var(--primitive-backdrop)]\",\n \"transition-opacity duration-[120ms] ease-out\",\n \"data-[starting-style]:opacity-0 data-[ending-style]:opacity-0\",\n \"motion-reduce:transition-none\",\n);\n\n/**\n * Command palettes are opened hundreds of times a day, often via keyboard\n * shortcut — per Emil Kowalski's frequency rule, high-frequency/keyboard\n * actions get no meaningful animation. Opacity-only, ~80ms, nearly\n * imperceptible rather than a jarring cut.\n */\nconst popupMotionClass = cn(\n \"transition-opacity duration-[80ms] ease-out\",\n \"data-[starting-style]:opacity-0 data-[ending-style]:opacity-0\",\n \"motion-reduce:transition-none\",\n);\n\nfunction adaptOpenChange(\n onOpenChange?: (open: boolean) => void,\n): ((open: boolean, details: unknown) => void) | undefined {\n if (!onOpenChange) return undefined;\n return (open) => onOpenChange(open);\n}\n\nexport type CommandItem = {\n id: string;\n label: string;\n description?: string;\n icon?: React.ReactNode;\n shortcut?: string[];\n keywords?: string[];\n disabled?: boolean;\n onSelect?: () => void;\n /** Opens a submenu page instead of selecting when set. */\n items?: CommandGroup[];\n /** Heading shown at the top of the submenu page this item opens. */\n pageTitle?: string;\n};\n\nexport type CommandGroup = {\n heading?: string;\n items: CommandItem[];\n};\n\nexport interface CommandProps {\n groups: CommandGroup[];\n placeholder?: string;\n emptyMessage?: React.ReactNode;\n /** Client-side filter against label/description/keywords. */\n filter?: boolean;\n onSelect?: (item: CommandItem) => void;\n /** Reports live query/nesting state — used by `CommandDialog` to decide whether Escape should back out of a submenu instead of closing. */\n onNavigationStateChange?: (state: { hasQuery: boolean; isNested: boolean }) => void;\n autoFocus?: boolean;\n showHints?: boolean;\n className?: string;\n listClassName?: string;\n \"aria-label\"?: string;\n}\n\ntype CommandPage = {\n title?: string;\n groups: CommandGroup[];\n};\n\nfunction matchesQuery(item: CommandItem, query: string) {\n if (!query) return true;\n const haystack = [item.label, item.description, item.id, ...(item.keywords ?? [])]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(query);\n}\n\nfunction filterPageGroups(groups: CommandGroup[], query: string): CommandGroup[] {\n if (!query) return groups;\n return groups\n .map((group) => ({\n ...group,\n items: group.items.filter((item) => matchesQuery(item, query)),\n }))\n .filter((group) => group.items.length > 0);\n}\n\nfunction KeyCap({ children }: { children: React.ReactNode }) {\n return (\n \n {children}\n \n );\n}\n\nexport function Command({\n groups,\n placeholder = \"Search...\",\n emptyMessage = \"No results found.\",\n filter = true,\n onSelect,\n onNavigationStateChange,\n autoFocus = false,\n showHints = true,\n className,\n listClassName,\n \"aria-label\": ariaLabel = \"Command menu\",\n}: CommandProps) {\n const inputRef = React.useRef(null);\n\n const [pages, setPages] = React.useState([{ groups }]);\n const [query, setQuery] = React.useState(\"\");\n\n React.useEffect(() => {\n setPages([{ groups }]);\n }, [groups]);\n\n const currentPage = pages[pages.length - 1]!;\n const isNested = pages.length > 1;\n const filterQuery = filter ? query.trim().toLowerCase() : \"\";\n const filteredGroups = React.useMemo(\n () => filterPageGroups(currentPage.groups, filterQuery),\n [currentPage, filterQuery],\n );\n const hasResults = filteredGroups.some((group) => group.items.length > 0);\n\n React.useEffect(() => {\n onNavigationStateChange?.({ hasQuery: query.trim().length > 0, isNested });\n }, [isNested, onNavigationStateChange, query]);\n\n React.useEffect(() => {\n if (autoFocus) inputRef.current?.focus({ preventScroll: true });\n }, [autoFocus]);\n\n const goBack = React.useCallback(() => {\n if (pages.length <= 1) return;\n setPages((current) => current.slice(0, -1));\n setQuery(\"\");\n }, [pages.length]);\n\n const selectItem = React.useCallback(\n (item: CommandItem) => {\n if (item.disabled) return;\n\n if (item.items && item.items.length > 0) {\n setPages((current) => [\n ...current,\n { title: item.pageTitle ?? item.label, groups: item.items! },\n ]);\n setQuery(\"\");\n // Keep focus on the input so the user can keep typing into the new page.\n requestAnimationFrame(() => inputRef.current?.focus({ preventScroll: true }));\n return;\n }\n\n item.onSelect?.();\n onSelect?.(item);\n },\n [onSelect],\n );\n\n const handleInputKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === \"Escape\") {\n if (query) {\n // Clear query first — do not let the surrounding Dialog close.\n event.preventDefault();\n event.stopPropagation();\n setQuery(\"\");\n return;\n }\n if (isNested) {\n event.preventDefault();\n event.stopPropagation();\n goBack();\n }\n return;\n }\n\n if (event.key === \"Backspace\" && !query && isNested) {\n event.preventDefault();\n goBack();\n }\n },\n [goBack, isNested, query],\n );\n\n return (\n setQuery(next)}\n autoHighlight=\"always\"\n keepHighlight\n >\n
\n
\n {isNested ? (\n \n \n \n ) : (\n \n )}\n\n \n
\n\n \n {hasResults ? (\n filteredGroups.map((group, groupIndex) => (\n 0 ? \"mt-1\" : undefined}\n >\n {group.heading ? (\n \n {group.heading}\n \n ) : null}\n {group.items.map((item) => (\n selectItem(item)}\n className={cn(\n \"flex w-full min-h-9 cursor-default select-none items-center gap-2.5 rounded-[var(--primitive-radius-item)] px-2.5 py-2 text-left outline-none\",\n \"text-[13.5px] text-[var(--foreground)] transition-colors duration-100 ease-out\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-40\",\n \"data-[highlighted]:bg-[var(--primitive-surface-selected)]\",\n )}\n >\n {item.icon ? (\n \n {item.icon}\n \n ) : null}\n \n \n {item.label}\n \n {item.description ? (\n \n {item.description}\n \n ) : null}\n \n {item.items && item.items.length > 0 ? (\n \n ) : item.shortcut && item.shortcut.length > 0 ? (\n \n {item.shortcut.map((key, keyIndex) => (\n {key}\n ))}\n \n ) : null}\n \n ))}\n \n ))\n ) : (\n \n {emptyMessage}\n
\n )}\n \n\n {showHints ? (\n
\n \n \n \n \n Select\n \n \n \n \n Navigate\n \n {isNested ? (\n \n esc\n Back\n \n ) : null}\n
\n ) : null}\n \n \n );\n}\n\nexport interface CommandDialogProps extends CommandProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n trigger?: React.ReactNode;\n /** Global shortcut that toggles the dialog, e.g. `{ key: \"k\", meta: true }`. */\n shortcut?: { key: string; meta?: boolean; ctrl?: boolean };\n /** Close the dialog after a leaf item is selected. */\n closeOnSelect?: boolean;\n}\n\nfunction useControllableOpen(\n controlled: boolean | undefined,\n defaultOpen: boolean,\n onOpenChange?: (open: boolean) => void,\n) {\n const [uncontrolled, setUncontrolled] = React.useState(defaultOpen);\n const open = controlled ?? uncontrolled;\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (controlled === undefined) setUncontrolled(next);\n onOpenChange?.(next);\n },\n [controlled, onOpenChange],\n );\n return [open, setOpen] as const;\n}\n\nexport function CommandDialog({\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n trigger,\n shortcut,\n closeOnSelect = true,\n onSelect,\n ...commandProps\n}: CommandDialogProps) {\n const [open, setOpen] = useControllableOpen(openProp, defaultOpen, onOpenChange);\n\n React.useEffect(() => {\n if (!shortcut) return;\n\n function handleKeyDown(event: KeyboardEvent) {\n const key = shortcut!.key.toLowerCase();\n const metaOk = shortcut!.meta ? event.metaKey : true;\n const ctrlOk = shortcut!.ctrl ? event.ctrlKey : true;\n if (event.key.toLowerCase() === key && metaOk && ctrlOk) {\n event.preventDefault();\n setOpen(!open);\n }\n }\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, setOpen, shortcut]);\n\n const renderTrigger = () => {\n if (!trigger) return null;\n if (React.isValidElement(trigger)) {\n const nativeButton =\n typeof trigger.type === \"string\" ? trigger.type === \"button\" : true;\n return (\n \n );\n }\n return {trigger};\n };\n\n return (\n \n {renderTrigger()}\n \n \n \n \n Command menu\n {\n onSelect?.(item);\n if (closeOnSelect) setOpen(false);\n }}\n />\n \n \n \n \n );\n}\n" } ], "cssVars": { "theme": { "font-sans": "var(--font-satoshi, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif)", "font-display": "var(--font-cabinet, ui-sans-serif, system-ui, sans-serif)", "color-background": "var(--background)", "color-foreground": "var(--foreground)", "color-surface": "var(--surface)", "color-surface-muted": "var(--surface-muted)", "color-border": "var(--border)", "color-border-strong": "var(--border-strong)", "color-muted": "var(--muted)", "color-muted-foreground": "var(--muted-foreground)", "color-ring": "var(--ring)", "color-chili-50": "#fff1ee", "color-chili-100": "#ffe2db", "color-chili-200": "#ffa896", "color-chili-300": "#ff8a73", "color-chili-400": "#f15a45", "color-chili-500": "#cd1c18", "color-chili-600": "#b31614", "color-chili-700": "#9b1313", "color-chili-800": "#6a0d0e", "color-chili-900": "#38000a", "color-chili-950": "#1f0006", "color-primitive-surface-elevated": "var(--primitive-surface-elevated)", "color-primitive-surface-overlay": "var(--primitive-surface-overlay)", "color-primitive-surface-hover": "var(--primitive-surface-hover)", "color-primitive-surface-active": "var(--primitive-surface-active)", "color-primitive-surface-selected": "var(--primitive-surface-selected)", "color-primitive-border-subtle": "var(--primitive-border-subtle)", "color-primitive-ring": "var(--primitive-ring)", "color-primitive-text-secondary": "var(--primitive-text-secondary)", "color-primitive-text-placeholder": "var(--primitive-text-placeholder)", "color-primitive-text-inverse": "var(--primitive-text-inverse)", "color-primitive-destructive": "var(--primitive-destructive)", "color-primitive-destructive-hover": "var(--primitive-destructive-hover)", "color-primitive-destructive-active": "var(--primitive-destructive-active)", "color-primitive-destructive-foreground": "var(--primitive-destructive-foreground)", "color-primitive-destructive-surface": "var(--primitive-destructive-surface)", "color-primitive-destructive-border": "var(--primitive-destructive-border)", "color-primitive-success": "var(--primitive-success)", "color-primitive-warning": "var(--primitive-warning)", "color-primitive-info": "var(--primitive-info)", "color-primitive-control-solid": "var(--primitive-control-solid)", "color-primitive-control-solid-hover": "var(--primitive-control-solid-hover)", "color-primitive-control-solid-active": "var(--primitive-control-solid-active)", "color-primitive-control-solid-foreground": "var(--primitive-control-solid-foreground)", "color-primitive-chart-1": "var(--primitive-chart-1)", "color-primitive-chart-2": "var(--primitive-chart-2)", "color-primitive-chart-3": "var(--primitive-chart-3)", "color-primitive-chart-4": "var(--primitive-chart-4)", "color-primitive-chart-5": "var(--primitive-chart-5)", "color-primitive-chart-6": "var(--primitive-chart-6)", "radius-primitive": "var(--primitive-radius)", "radius-primitive-control": "var(--primitive-radius-control)", "radius-primitive-control-sm": "var(--primitive-radius-control-sm)", "radius-primitive-surface": "var(--primitive-radius-surface)", "radius-primitive-item": "var(--primitive-radius-item)", "font-primitive-sans": "var(--primitive-font-sans)", "font-primitive-display": "var(--primitive-font-display)", "font-primitive-mono": "var(--primitive-font-mono)", "spacing-primitive-control-height-sm": "var(--primitive-control-height-sm)", "spacing-primitive-control-height-md": "var(--primitive-control-height-md)", "spacing-primitive-control-height-lg": "var(--primitive-control-height-lg)" }, "light": { "background": "#fafafa", "foreground": "#0a0a0a", "surface": "#ffffff", "surface-muted": "#f4f4f5", "border": "rgba(10, 10, 10, 0.08)", "border-strong": "rgba(10, 10, 10, 0.16)", "muted": "#f4f4f5", "muted-foreground": "#52525b", "ring": "#cd1c18", "pattern-fg": "rgba(10, 10, 10, 0.07)", "llb-primary": "#18181b", "llb-primary-fg": "#ffffff", "llb-success": "#1f883d", "llb-error": "#cf222e", "primitive-surface-elevated": "#ffffff", "primitive-surface-overlay": "#ffffff", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 4%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 7%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 6%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#24292d", "primitive-control-solid-hover": "#2c3237", "primitive-control-solid-active": "#1e2326", "primitive-control-solid-foreground": "#ffffff", "primitive-chart-1": "#2a78d6", "primitive-chart-2": "#1baf7a", "primitive-chart-3": "#eda100", "primitive-chart-4": "#008300", "primitive-chart-5": "#4a3aa7", "primitive-chart-6": "#e34948", "primitive-text-inverse": "#ffffff", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#dc2626", "primitive-destructive-hover": "#b91c1c", "primitive-destructive-active": "#991b1b", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(220, 38, 38, 0.10)", "primitive-destructive-border": "rgba(220, 38, 38, 0.45)", "primitive-success": "#047857", "primitive-success-surface": "rgba(4, 120, 87, 0.10)", "primitive-success-border": "rgba(4, 120, 87, 0.45)", "primitive-warning": "#b45309", "primitive-warning-surface": "rgba(180, 83, 9, 0.10)", "primitive-warning-border": "rgba(180, 83, 9, 0.45)", "primitive-info": "#0369a1", "primitive-info-surface": "rgba(3, 105, 161, 0.10)", "primitive-info-border": "rgba(3, 105, 161, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.06), 0 8px 24px -12px rgba(0, 0, 0, 0.08)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.06), 0 18px 48px -24px rgba(0, 0, 0, 0.35)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.08), 0 24px 64px -28px rgba(0, 0, 0, 0.42)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" }, "dark": { "background": "#0a0a0b", "foreground": "#f5f5f6", "surface": "#111113", "surface-muted": "#18181b", "border": "rgba(255, 255, 255, 0.08)", "border-strong": "rgba(255, 255, 255, 0.14)", "muted": "#1c1c1f", "muted-foreground": "#a1a1aa", "ring": "#cd1c18", "pattern-fg": "rgba(255, 255, 255, 0.06)", "llb-primary": "#f5f5f6", "llb-primary-fg": "#0a0a0b", "llb-success": "#2da44e", "llb-error": "#f85149", "primitive-surface-elevated": "#0f0f10", "primitive-surface-overlay": "#141415", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 5%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 10%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 8%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#f5f3ee", "primitive-control-solid-hover": "#ffffff", "primitive-control-solid-active": "#e8e4da", "primitive-control-solid-foreground": "#151719", "primitive-chart-1": "#3987e5", "primitive-chart-2": "#199e70", "primitive-chart-3": "#c98500", "primitive-chart-4": "#008300", "primitive-chart-5": "#9085e9", "primitive-chart-6": "#e66767", "primitive-text-inverse": "#151719", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#e11d2e", "primitive-destructive-hover": "#c1121f", "primitive-destructive-active": "#a4161a", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(225, 29, 46, 0.12)", "primitive-destructive-border": "rgba(225, 29, 46, 0.52)", "primitive-success": "#34d399", "primitive-success-surface": "rgba(52, 211, 153, 0.12)", "primitive-success-border": "rgba(52, 211, 153, 0.45)", "primitive-warning": "#fbbf24", "primitive-warning-surface": "rgba(251, 191, 36, 0.12)", "primitive-warning-border": "rgba(251, 191, 36, 0.45)", "primitive-info": "#38bdf8", "primitive-info-surface": "rgba(56, 189, 248, 0.12)", "primitive-info-border": "rgba(56, 189, 248, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px -12px rgba(0, 0, 0, 0.6)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 56px -28px rgba(0, 0, 0, 0.72)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.45), 0 28px 72px -32px rgba(0, 0, 0, 0.8)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" } }, "css": { "@layer base": { ":where([data-wensity-primitive])": { "font-family": "var(--primitive-font-sans)" } }, "@utility scrollbar-hidden": { "scrollbar-width": "none", "-ms-overflow-style": "none", "&::-webkit-scrollbar": { "display": "none" } }, "@keyframes wensity-marquee-x": { "from": { "transform": "translate3d(0, 0, 0)" }, "to": { "transform": "translate3d(-50%, 0, 0)" } }, "@keyframes wensity-marquee-x-reverse": { "from": { "transform": "translate3d(-50%, 0, 0)" }, "to": { "transform": "translate3d(0, 0, 0)" } }, "@keyframes wensity-morph-rot-cw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(360deg)" } }, "@keyframes wensity-morph-rot-ccw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(-360deg)" } } }, "docs": "Free Wensity component. Installs to @components/wensity/command.tsx. For Pro components and updates, use pnpm dlx wensity@latest add command.", "categories": [ "UI Primitives", "wensity", "free" ], "meta": { "wensity": { "slug": "command", "access": "free", "category": "UI Primitives", "kind": "component", "componentUrl": "https://ui.wensity.com/primitives/command", "shadcnUrl": "https://ui.wensity.com/r/command", "cliInstall": "pnpm dlx wensity@latest add command" } } }