{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command",
  "title": "Command",
  "description": "shadcn/ui(new-york-v4) command의 유니버설(웹+네이티브 한 벌 소스) 포팅. React state + context + 항목 레지스트리(useEffect 등록)로 직접 재구현한다. 포팅 판단(원본 대비 차이):",
  "dependencies": [
    "@unikit-bridge/ui"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/command.tsx",
      "content": "// Command — shadcn/ui（new-york-v4） command.tsx 유니버설 포팅 （사이클 40, T-1c）.\n// 원본: shadcn-ref/ui/command.tsx — cmdk 엔진을 제거하고 필터링키보드 선택을\n// React state + context + 항목 레지스트리（useEffect 등록）로 직접 재구현한다.\n//\n// 포팅 판단（원본 대비 차이）:\n// - cmdk 제거: 필터는 대소문자 무시 단어별 부분문자열 일치（검색어의 공백 구분\n//   토큰이 valuekeywords에 모두 포함돼야 매치 — cmdk fuzzy 스코어링의 단순화）.\n//   비매칭 항목은 렌더 제외（cmdk의 hidden 처리와 같은 가시 결과）. shouldFilter\n//   loop prop은 유지하고 커스텀 filter 함수는 미지원.\n// - CommandDialog 이식（G3 F-5 반영 — 설계 §3 명시 항목） — Dialog +\n//   DialogContent 조합. 원본의 cmdk 그룹 셀렉터 클래스（［&_[cmdk-item］ 등）는\n//   radix 제거 원칙상 소실, sr-only 헤더는 accessibilityLabel 근사（관행）.\n// - CommandLoading은 원본 new-york-v4 참조에 없어 미이식.\n// - 키보드 탐색（웹）: CommandInput onKeyDown（BridgeKeyEvent）으로 ArrowUp/Down\n//   （선택 이동, loop 시 순환）Enter（선택 항목 onSelect 확정）Escape（필터 비움 —\n//   빈 상태면 무동작, 닫기는 소비측 Dialog/Popover 역할）. 필터가 바뀌면 선택은\n//   첫 매칭 항목으로 되돌아간다（cmdk 동작）. 미선택 시 첫 매칭 항목이 유효 선택.\n// - 네이티브 한계: onKeyDown은 RN onKeyPress（{key}） 래핑（bridge/types.ts）이라\n//   변환 불가 키（IME 조합 등）는 발화되지 않고 preventDefault가 no-op다 —\n//   터치 선택（onPress → onSelect）이 네이티브 기본 경로.\n// - 선택 항목 자동 스크롤（cmdk scrollIntoView）은 항목 측정 계약이 없어 미지원.\n// - SearchIcon — 브리지 lucide 아이콘 프리미티브（primitives 경유）.\n// - 원본 data-selected/data-disabled［cmdk-group-heading］ 그룹 셀렉터 클래스는\n//   네이티브에 data 속성이 없어 조건부 클래스heading Text 직접 지정으로 전개.\n// - 그룹 숨김: 소속 항목이 전부 비매칭이면 그룹 컨테이너에 hidden 클래스 —\n//   자식을 언마운트하지 않아（레지스트리 등록 유지） 필터 복귀에 즉시 대응한다.\n//   마운트 전（version 0, 초기 렌더SSR）엔 표시 상태로 그린다（거짓 숨김 방지）.\n// - CommandList는 브리지 ScrollView 프리미티브（웹 overflow div / RN ScrollView）.\n// - 임의값 금지: 원본 max-h-［300px］ → max-h-72（288px） 근사.\n// - 접근성: 목록 listbox항목 option + aria-selected, 입력 래퍼 combobox +\n//   aria-expanded/aria-controls（useId로 목록 nativeID와 연결）. aria 2종은\n//   브리지 ViewProps에 계약이 없어 확장 타입 스프레드로 웹 전달（웹 View는\n//   rest를 div에 그대로 실음） — 네이티브에서 폐기, 계약 확장 필요（보고）.\n// - 항목의 문자열 children은 내부 Text로 감싼다（네이티브 View 내 문자열\n//   크래시 방지 — button-grouppagination 관행）. value 미지정 시 최상위 문자열\n//   children에서 유도（cmdk textContent 유추의 근사, 중첩 요소 텍스트는 미수집）.\n// - 루트의 text-popover-foreground는 웹 상속용으로 유지（커스텀 children 대응）,\n//   문자열 children 경로는 Text에 직접 지정한다（이중 지정 원칙）.\nimport {\n  Children,\n  createContext,\n  forwardRef,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n  type ReactNode,\n} from \"react\";\nimport {\n  Pressable,\n  ScrollView,\n  SearchIcon,\n  Text,\n  TextInput,\n  View,\n  type BridgeKeyEvent,\n  type BridgePressableRef,\n  type BridgeScrollViewRef,\n  type BridgeTextRef,\n  type BridgeTextInputRef,\n  type BridgeViewRef,\n  type PressableProps,\n  type ScrollViewProps,\n  type TextProps,\n  type TextInputProps,\n  type ViewProps,\n} from \"@unikit-bridge/ui/primitives\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Dialog, DialogContent, type DialogProps } from \"./dialog\";\n\n/** 항목 등록 정보 — 키보드 탐색필터 판정의 단일 출처 */\ntype CommandItemMeta = {\n  /** 등록 인스턴스 ID（useId） — value 중복과 무관한 식별 */\n  id: string;\n  value: string;\n  keywords?: string[];\n  disabled: boolean;\n  /** 소속 그룹 ID — 그룹 밖 항목은 undefined */\n  groupId?: string;\n  onSelect?: (value: string) => void;\n};\n\ntype CommandContextValue = {\n  filter: string;\n  setFilter: (next: string) => void;\n  shouldFilter: boolean;\n  /** 등록된 전체 항목（등록 순） */\n  items: CommandItemMeta[];\n  /** 등록（해제 함수 반환） — 항목 마운트와 무관하게 레지스트리를 유지한다 */\n  registerItem: (meta: CommandItemMeta) => () => void;\n  /** 유효 선택값 — 미선택비매칭이면 첫 매칭 항목（cmdk 동작） */\n  activeValue: string | null;\n  select: (value: string | null) => void;\n  /** 입력창 키 처리（ArrowUp/DownEnterEscape） */\n  handleKeyDown: (event: BridgeKeyEvent) => void;\n  /** aria-controls ↔ CommandList nativeID 연결용 */\n  listId: string;\n  /** 레지스트리 변경 횟수 — 0이면 마운트 전（초기 렌더SSR 낙관 상태） */\n  version: number;\n};\n\nconst CommandContext = createContext<CommandContextValue | null>(null);\n\nfunction useCommandContext(component: string): CommandContextValue {\n  const ctx = useContext(CommandContext);\n  if (!ctx) {\n    throw new Error(`<${component}>는 <Command> 안에서 사용해야 합니다.`);\n  }\n  return ctx;\n}\n\n/** 그룹 소속 전달 — CommandGroup이 ID를 제공, 항목 등록 meta에 싣는다 */\nconst CommandGroupContext = createContext<string | undefined>(undefined);\n\n/** 필터 일치 — shouldFilter=false 또는 빈 검색어면 전체 매치, 그 외엔 모든\n *  공백 구분 토큰이 valuekeywords 결합 문자열에 포함돼야 한다 */\nfunction matchesFilter(\n  filter: string,\n  shouldFilter: boolean,\n  value: string,\n  keywords?: string[],\n): boolean {\n  if (!shouldFilter) return true;\n  const tokens = filter.trim().toLowerCase().split(/\\s+/).filter(Boolean);\n  if (tokens.length === 0) return true;\n  const haystack = [value, ...(keywords ?? [])].join(\" \").toLowerCase();\n  return tokens.every((token) => haystack.includes(token));\n}\n\n/** value 미지정 항목의 유도값 — 최상위 문자열 children 결합 */\nfunction textValueOf(nodes: ReactNode): string {\n  return Children.toArray(nodes)\n    .filter((child): child is string => typeof child === \"string\")\n    .join(\" \")\n    .trim();\n}\n\nexport type CommandProps = ViewProps & {\n  /** 키보드 선택값（제어） — 미지정 시 내부 상태 */\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  /** 필터 적용 여부（기본 true） — false면 전체 항목 표시 */\n  shouldFilter?: boolean;\n  /** 방향키 끝 도달 시 순환（기본 false） */\n  loop?: boolean;\n};\n\nconst Command = forwardRef<BridgeViewRef, CommandProps>(\n  (\n    {\n      value,\n      defaultValue,\n      onValueChange,\n      shouldFilter = true,\n      loop = false,\n      className,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    // 비제어 선택 상태 — 제어（value 지정）면 무시된다.\n    const [internalSelected, setInternalSelected] = useState<string | null>(\n      defaultValue ?? null,\n    );\n    const selected = value !== undefined ? value : internalSelected;\n    const [filter, setInternalFilter] = useState(\"\");\n\n    // 항목 레지스트리 — 등록/해제마다 version을 올려 EmptyGroup이 재평가하게\n    // 한다. 루트 마운트 효과로 version ≥ 1을 보장（0 = 마운트 전 낙관 상태）.\n    const registry = useRef(new Map<string, CommandItemMeta>());\n    const [version, setVersion] = useState(0);\n    useEffect(() => {\n      setVersion((v) => v + 1);\n    }, []);\n    const registerItem = useCallback((meta: CommandItemMeta) => {\n      const existing = registry.current.get(meta.id);\n      registry.current.set(meta.id, meta);\n      const structuralSame =\n        existing &&\n        existing.value === meta.value &&\n        existing.disabled === meta.disabled &&\n        existing.groupId === meta.groupId &&\n        existing.keywords?.join(\"\\0\") === meta.keywords?.join(\"\\0\");\n      if (!structuralSame) {\n        setVersion((v) => v + 1);\n      }\n      return () => {\n        if (registry.current.delete(meta.id)) {\n          setVersion((v) => v + 1);\n        }\n      };\n    }, []);\n    const items = useMemo(\n      () => Array.from(registry.current.values()),\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n      [version],\n    );\n\n    const select = useCallback(\n      (next: string | null) => {\n        if (value === undefined) setInternalSelected(next);\n        if (next !== null && next !== selected) onValueChange?.(next);\n      },\n      [value, selected, onValueChange],\n    );\n\n    // 필터 갱신 — 필터가 바뀌면 선택을 첫 매칭 항목으로 되돌린다（cmdk 동작）.\n    const setFilter = useCallback(\n      (next: string) => {\n        setInternalFilter(next);\n        if (value === undefined) setInternalSelected(null);\n      },\n      [value],\n    );\n\n    // 매칭비활성 제외한 탐색 대상과 유효 선택값（미선택 시 첫 매칭）.\n    const selectableItems = useMemo(\n      () =>\n        items.filter(\n          (item) =>\n            !item.disabled &&\n            matchesFilter(filter, shouldFilter, item.value, item.keywords),\n        ),\n      [items, filter, shouldFilter],\n    );\n    const activeValue = useMemo(\n      () =>\n        selected !== null &&\n        selectableItems.some((item) => item.value === selected)\n          ? selected\n          : (selectableItems[0]?.value ?? null),\n      [selected, selectableItems],\n    );\n\n    const moveSelection = useCallback(\n      (direction: 1 | -1) => {\n        if (selectableItems.length === 0) return;\n        const index = selectableItems.findIndex(\n          (item) => item.value === activeValue,\n        );\n        const next =\n          index === -1\n            ? 0\n            : loop\n              ? (index + direction + selectableItems.length) %\n                selectableItems.length\n              : Math.min(\n                  selectableItems.length - 1,\n                  Math.max(0, index + direction),\n                );\n        select(selectableItems[next].value);\n      },\n      [selectableItems, activeValue, loop, select],\n    );\n\n    const handleKeyDown = useCallback(\n      (event: BridgeKeyEvent) => {\n        switch (event.key) {\n          case \"ArrowUp\":\n            event.preventDefault();\n            moveSelection(-1);\n            break;\n          case \"ArrowDown\":\n            event.preventDefault();\n            moveSelection(1);\n            break;\n          case \"Enter\": {\n            const chosen = selectableItems.find(\n              (item) => item.value === activeValue,\n            );\n            if (chosen) {\n              event.preventDefault();\n              select(chosen.value);\n              chosen.onSelect?.(chosen.value);\n            }\n            break;\n          }\n          case \"Escape\":\n            // 필터 비움 — 이미 비었으면 무동작（닫기는 소비측 역할）\n            event.preventDefault();\n            if (filter !== \"\") setFilter(\"\");\n            break;\n        }\n      },\n      [moveSelection, selectableItems, activeValue, select, filter, setFilter],\n    );\n\n    const baseId = useId();\n    const listId = `${baseId}-command-list`;\n    const contextValue = useMemo<CommandContextValue>(\n      () => ({\n        filter,\n        setFilter,\n        shouldFilter,\n        items,\n        registerItem,\n        activeValue,\n        select,\n        handleKeyDown,\n        listId,\n        version,\n      }),\n      [\n        filter,\n        setFilter,\n        shouldFilter,\n        items,\n        registerItem,\n        activeValue,\n        select,\n        handleKeyDown,\n        listId,\n        version,\n      ],\n    );\n    return (\n      <CommandContext.Provider value={contextValue}>\n        <View\n          ref={ref}\n          className={cn(\n            \"h-full w-full overflow-hidden rounded-md bg-popover text-popover-foreground\",\n            className,\n          )}\n          {...props}\n        >\n          {children}\n        </View>\n      </CommandContext.Provider>\n    );\n  },\n);\nCommand.displayName = \"Command\";\n\nexport type CommandInputProps = Omit<\n  TextInputProps,\n  \"value\" | \"defaultValue\" | \"onChangeText\"\n> & {\n  /** 검색창 래퍼（테두리높이패딩） 클래스 — 입력 요소 자체가 아니다（포팅 판단） */\n  className?: string;\n};\n\nconst CommandInput = forwardRef<BridgeTextInputRef, CommandInputProps>(\n  (\n    {\n      className,\n      placeholder,\n      editable,\n      disabled,\n      autoFocus,\n      inputMode,\n      onKeyDown,\n      ...props\n    },\n    ref,\n  ) => {\n    const command = useCommandContext(\"CommandInput\");\n    // combobox aria 2종 — 브리지 ViewProps 미계약 속성. 웹 View은 rest를 div에\n    // 그대로 실어 보내고（bridge/web.tsx） 네이티브 구현은 무시폐기한다.\n    // 계약 확장（ViewProps에 aria-expandedaria-controls 추가） 필요 — 보고 예정.\n    const comboboxAria: {\n      \"aria-expanded\": boolean;\n      \"aria-controls\": string;\n    } = {\n      \"aria-expanded\": true,\n      \"aria-controls\": command.listId,\n    };\n    return (\n      <View\n        accessibilityRole=\"combobox\"\n        {...comboboxAria}\n        className={cn(\n          // 원본 flex h-9 items-center gap-2 border-b px-3 — 높이는 양 플랫폼 공통이라\n          // web: 접두 없이 원본값을 그대로 쓴다.\n          \"flex-none flex-row items-center gap-2 border-b border-border px-3 h-9\",\n          className,\n        )}\n      >\n        {/* 원본 아이콘: size-4 shrink-0 opacity-50 — 색은 루트 text-popover-foreground 상속 */}\n        <SearchIcon className=\"size-4 shrink-0 text-popover-foreground opacity-50\" />\n        <TextInput\n          {...props}\n          ref={ref}\n          value={command.filter}\n          onChangeText={command.setFilter}\n          onKeyDown={(event) => {\n            command.handleKeyDown(event);\n            onKeyDown?.(event);\n          }}\n          placeholder={placeholder}\n          editable={disabled ? false : editable}\n          disabled={disabled}\n          aria-disabled={disabled ? true : undefined}\n          autoFocus={autoFocus}\n          inputMode={inputMode ?? \"search\"}\n          className=\"h-full flex-1 font-sans text-sm text-popover-foreground placeholder:text-muted-foreground web:outline-none disabled:opacity-50\"\n        />\n      </View>\n    );\n  },\n);\nCommandInput.displayName = \"CommandInput\";\n\nexport type CommandListProps = ScrollViewProps;\n\nconst CommandList = forwardRef<BridgeScrollViewRef, CommandListProps>(\n  ({ className, children, nativeID, ...props }, ref) => {\n    const command = useCommandContext(\"CommandList\");\n    return (\n      <ScrollView\n        ref={ref}\n        accessibilityRole=\"listbox\"\n        nativeID={nativeID ?? command.listId}\n        className={cn(\"max-h-72 web:scroll-py-1\", className)}\n        {...props}\n      >\n        {children}\n      </ScrollView>\n    );\n  },\n);\nCommandList.displayName = \"CommandList\";\n\nexport type CommandEmptyProps = ViewProps;\n\nconst CommandEmpty = forwardRef<BridgeViewRef, CommandEmptyProps>(\n  ({ className, children, ...props }, ref) => {\n    const command = useCommandContext(\"CommandEmpty\");\n    const hasMatch = command.items.some((item) =>\n      matchesFilter(\n        command.filter,\n        command.shouldFilter,\n        item.value,\n        item.keywords,\n      ),\n    );\n    // 마운트 전（version 0）엔 레지스트리가 비어 거짓 빈 상태가 된다 — 숨김.\n    if (hasMatch || command.version === 0) return null;\n    return (\n      <View ref={ref} className={cn(\"py-6\", className)} {...props}>\n        {/* 문자열 children 권장（원본 그대로 text-center text-sm, 색은 루트 상속） */}\n        <Text className=\"text-center font-sans text-sm text-popover-foreground\">\n          {children}\n        </Text>\n      </View>\n    );\n  },\n);\nCommandEmpty.displayName = \"CommandEmpty\";\n\nexport type CommandGroupProps = ViewProps & {\n  /** 그룹 라벨（heading） — 문자열 children 권장 */\n  heading?: ReactNode;\n};\n\nconst CommandGroup = forwardRef<BridgeViewRef, CommandGroupProps>(\n  ({ heading, className, children, ...props }, ref) => {\n    const command = useCommandContext(\"CommandGroup\");\n    const groupId = useId();\n    const hasVisible = command.items.some(\n      (item) =>\n        item.groupId === groupId &&\n        matchesFilter(\n          command.filter,\n          command.shouldFilter,\n          item.value,\n          item.keywords,\n        ),\n    );\n    return (\n      <CommandGroupContext.Provider value={groupId}>\n        <View\n          ref={ref}\n          // 소속 항목 전부 비매칭 → hidden（언마운트 아님, 등록 유지）.\n          // 마운트 전（version 0）엔 표시로 그린다（SSR초기 낙관 렌더）.\n          className={cn(\n            \"overflow-hidden p-1\",\n            command.version > 0 && !hasVisible && \"hidden\",\n            className,\n          )}\n          {...props}\n        >\n          {heading != null && (\n            <Text className=\"px-2 py-1.5 font-sans text-xs font-medium text-muted-foreground web:whitespace-nowrap\">\n              {heading}\n            </Text>\n          )}\n          {children}\n        </View>\n      </CommandGroupContext.Provider>\n    );\n  },\n);\nCommandGroup.displayName = \"CommandGroup\";\n\nexport type CommandItemProps = PressableProps & {\n  /** 항목값 — 필터선택 매칭에 쓰인다（미지정 시 문자열 children에서 유도） */\n  value?: string;\n  /** 필터 추가 검색어（cmdk keywords） */\n  keywords?: string[];\n  /** 선택 확정 콜백 — 터치 onPress와 웹 Enter가 호출（cmdk와 동일 시그니처） */\n  onSelect?: (value: string) => void;\n  children?: ReactNode;\n};\n\nconst CommandItem = forwardRef<BridgePressableRef, CommandItemProps>(\n  (\n    {\n      value,\n      keywords,\n      disabled,\n      onSelect,\n      onPress,\n      className,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const command = useCommandContext(\"CommandItem\");\n    const groupId = useContext(CommandGroupContext);\n    const id = useId();\n    const resolvedValue = value ?? textValueOf(children);\n\n    // 레지스트리 등록 — 비매칭으로 렌더 제외돼도 컴포넌트는 마운트 상태라\n    // 등록이 유지되고, 필터 복귀 시 즉시 다시 나타난다.\n    // onSelect/keywords를 ref에 보관해 인라인 콜백·배열 리터럴로 매 렌더\n    // 레지스트리 해제→재등록(version 증가)이 발생하지 않게 한다.\n    const onSelectRef = useRef(onSelect);\n    onSelectRef.current = onSelect;\n    const keywordsRef = useRef(keywords);\n    keywordsRef.current = keywords;\n    const keywordsKey = keywords?.join(\"\\0\") ?? \"\";\n    const registerItem = command.registerItem;\n    useEffect(() => {\n      const meta: CommandItemMeta = {\n        id,\n        value: resolvedValue,\n        keywords: keywordsRef.current,\n        disabled: disabled === true,\n        groupId,\n        onSelect: (selectedValue) => onSelectRef.current?.(selectedValue),\n      };\n      return registerItem(meta);\n    }, [registerItem, id, resolvedValue, keywordsKey, disabled, groupId]);\n\n    // 비매칭 항목은 렌더 제외 — 원본 cmdk hidden 처리와 같은 가시 결과.\n    if (\n      !matchesFilter(\n        command.filter,\n        command.shouldFilter,\n        resolvedValue,\n        keywords,\n      )\n    ) {\n      return null;\n    }\n\n    const selected = command.activeValue === resolvedValue;\n    // 문자열 children은 Text로 감싼다（네이티브 View 내 문자열 크래시 방지）.\n    // 커스텀 children은 그대로 렌더 — 글자색은 사용처 Text에 지정한다.\n    const labelClass = cn(\n      \"font-sans text-sm web:select-none\",\n      selected ? \"text-accent-foreground\" : \"text-popover-foreground\",\n    );\n    const content = Children.map(children, (child) =>\n      typeof child === \"string\" || typeof child === \"number\" ? (\n        <Text className={labelClass}>{child}</Text>\n      ) : (\n        child\n      ),\n    );\n\n    return (\n      <Pressable\n        ref={ref}\n        accessibilityRole=\"option\"\n        aria-selected={selected}\n        disabled={disabled}\n        onPress={() => {\n          // 터치 선택 — 네이티브 기본 경로\n          command.select(resolvedValue);\n          onSelect?.(resolvedValue);\n          onPress?.();\n        }}\n        className={cn(\n          \"relative flex-row items-center gap-2 rounded-sm px-2 py-1.5 web:cursor-default web:outline-none web:select-none\",\n          // 원본 data-［selected=true］data-［disabled=true］ 토큰\n          selected && \"bg-accent\",\n          disabled && \"web:pointer-events-none opacity-50\",\n          className,\n        )}\n        {...props}\n      >\n        {content}\n      </Pressable>\n    );\n  },\n);\nCommandItem.displayName = \"CommandItem\";\n\nexport type CommandShortcutProps = TextProps;\n\nconst CommandShortcut = forwardRef<BridgeTextRef, CommandShortcutProps>(\n  ({ className, ...props }, ref) => (\n    <Text\n      ref={ref}\n      className={cn(\n        \"ml-auto font-sans text-xs tracking-widest text-muted-foreground\",\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nCommandShortcut.displayName = \"CommandShortcut\";\n\nexport type CommandSeparatorProps = ViewProps;\n\nconst CommandSeparator = forwardRef<BridgeViewRef, CommandSeparatorProps>(\n  ({ className, ...props }, ref) => (\n    <View ref={ref} className={cn(\"-mx-1 h-px bg-border\", className)} {...props} />\n  ),\n);\nCommandSeparator.displayName = \"CommandSeparator\";\n\n// ---------------------------------------------------------------------------\n// CommandDialog — command 목록을 다이얼로그로 싣는 조합（원본 shadcn API\n// 유지 — 설계 §3 명시 항목, G3 F-5 반영 이식）.\n// ---------------------------------------------------------------------------\n\nexport type CommandDialogProps = DialogProps & {\n  /** 스크린리더용 대화상자 이름（원본 기본 \"Command Palette\"） */\n  title?: string;\n  /** 원본의 sr-only 설명 — 유니버설 sr-only 유틸 부재로 연결을 생략한다\n   *  （breadcrumb·dialog 포팅 관행） */\n  description?: string;\n  className?: string;\n  showCloseButton?: boolean;\n};\n\nconst CommandDialog = forwardRef<BridgeViewRef, CommandDialogProps>(\n  (\n    {\n      title = \"Command Palette\",\n      description: _description,\n      className,\n      showCloseButton = true,\n      children,\n      ...props\n    },\n    ref,\n  ) => (\n    <Dialog {...props}>\n      <DialogContent\n        ref={ref}\n        // 원본의 sr-only DialogHeader（제목·설명） 근사 — 제목은 오버레이\n        // 접근성 이름으로 전달한다（DialogContent → ModalOverlay）.\n        accessibilityLabel={title}\n        className={cn(\"gap-0 overflow-hidden p-0\", className)}\n        showCloseButton={showCloseButton}\n      >\n        {children}\n      </DialogContent>\n    </Dialog>\n  ),\n);\nCommandDialog.displayName = \"CommandDialog\";\n\nexport {\n  Command,\n  CommandDialog,\n  CommandInput,\n  CommandList,\n  CommandEmpty,\n  CommandGroup,\n  CommandItem,\n  CommandShortcut,\n  CommandSeparator,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/command.tsx"
    }
  ],
  "type": "registry:ui"
}
