{
  "name": "cli",
  "type": "registry:block",
  "description": "A complete interface for managing your Shadcn UI components.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "card",
    "command",
    "input",
    "popover",
    "scroll-area",
    "switch",
    "tooltip"
  ],
  "categories": [
    "tools",
    "development"
  ],
  "files": [
    {
      "path": "page.tsx",
      "content": "import { Suspense } from \"react\";\nimport { RegistryBrowser } from \"./_components/registry-browser\";\n\nexport default function Home() {\n\n\treturn (\n\t\t<main className=\"container py-6\">\n\t\t\t<div className=\"space-y-4\">\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"text-3xl font-bold\">Shipkit UI Browser</h1>\n\t\t\t\t\t<p className=\"text-muted-foreground\">\n\t\t\t\t\t\tBrowse and install components and blocks from various registries\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<Suspense fallback={<div>Loading...</div>}>\n\t\t\t\t\t<RegistryBrowser defaultRegistry=\"shadcn/ui\" />\n\t\t\t\t</Suspense>\n\t\t\t</div>\n\t\t</main>\n\t);\n}\n",
      "type": "registry:page",
      "target": "app/cli/page.tsx"
    },
    {
      "path": "_components/registry-browser.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/registry/default/components/ui/button\";\nimport { Card } from \"@/registry/default/components/ui/card\";\nimport {\n\tCommand,\n\tCommandEmpty,\n\tCommandGroup,\n\tCommandInput,\n\tCommandItem,\n\tCommandList,\n} from \"@/registry/default/components/ui/command\";\nimport { Input } from \"@/registry/default/components/ui/input\";\nimport {\n\tPopover,\n\tPopoverContent,\n\tPopoverTrigger,\n} from \"@/registry/default/components/ui/popover\";\nimport { ScrollArea } from \"@/registry/default/components/ui/scroll-area\";\nimport { Switch } from \"@/registry/default/components/ui/switch\"; // Import Switch component\nimport {\n\tTooltip,\n\tTooltipContent,\n\tTooltipProvider,\n\tTooltipTrigger,\n} from \"@/registry/default/components/ui/tooltip\";\nimport { useToast } from \"@/registry/default/hooks/use-toast\";\nimport { type Registry, type RegistryItem } from \"@/registry/default/lib/types\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport { CodeIcon } from \"@radix-ui/react-icons\";\nimport {\n\tBookOpen,\n\tCheck,\n\tChevronsUpDown,\n\tDownload,\n\tFileCode,\n\tFilter,\n\tLink,\n\tLoader2,\n\tTrash2,\n\tX,\n} from \"lucide-react\";\nimport { useRouter, useSearchParams } from \"next/navigation\";\nimport {\n\tuseCallback,\n\tuseEffect,\n\tuseMemo,\n\tuseState,\n\tuseTransition,\n} from \"react\";\nimport {\n\tgetInstalledComponents,\n\tgetProjectRoot,\n\tinstallComponent,\n} from \"../actions\";\nimport {\n\taddCustomRegistry,\n\tcategorizeItems,\n\tfetchItemDetails,\n\tfetchRegistryIndex,\n\tgetRegistries,\n\tremoveCustomRegistry,\n} from \"../lib/registry-service\";\nimport { AddRegistryDialog } from \"./add-registry-dialog\";\nimport { BlockPreview } from \"./block-preview\";\nimport { ComponentStats } from \"./component-stats\";\nimport { CopyButton } from \"./copy-button\";\nimport { FileTree } from \"./file-tree\";\nimport { Preview } from \"./preview\";\nimport { Terminal as TerminalComponent } from \"./terminal\";\n\ninterface RegistryBrowserProps {\n\tdefaultRegistry?: string;\n}\n\nexport function RegistryBrowser({ defaultRegistry }: RegistryBrowserProps) {\n\tconst router = useRouter();\n\tconst searchParams = useSearchParams();\n\tconst { toast } = useToast();\n\tconst [isPending, startTransition] = useTransition();\n\n\t// State\n\tconst [registries, setRegistries] = useState<Registry[]>([]);\n\tconst [selectedRegistry, setSelectedRegistry] = useState<Registry | null>(\n\t\tnull\n\t);\n\tconst [registryItems, setRegistryItems] = useState<RegistryItem[]>([]);\n\tconst [selectedItem, setSelectedItem] = useState<RegistryItem | null>(null);\n\tconst [itemDetails, setItemDetails] = useState<RegistryItem | null>(null);\n\tconst [searchQuery, setSearchQuery] = useState(\"\");\n\tconst [filterType, setFilterType] = useState<\"all\" | \"components\" | \"blocks\">(\n\t\t\"all\"\n\t);\n\tconst [filterCategory, setFilterCategory] = useState<string>(\"all\");\n\tconst [open, setOpen] = useState(false);\n\tconst [installing, setInstalling] = useState(false);\n\tconst [installOutput, setInstallOutput] = useState<string[]>([]);\n\tconst [overwrite, setOverwrite] = useState(false);\n\tconst [selectedFile, setSelectedFile] = useState<string>();\n\tconst [docsUrl, setDocsUrl] = useState<string | null>(null);\n\tconst [checkingDocs, setCheckingDocs] = useState(false);\n\tconst [installedComponents, setInstalledComponents] = useState<string[]>([]);\n\n\t// Load registries\n\tuseEffect(() => {\n\t\tconst loadRegistries = async () => {\n\t\t\ttry {\n\t\t\t\tconst regs = await getRegistries();\n\t\t\t\tsetRegistries(regs);\n\n\t\t\t\t// Set default registry\n\t\t\t\tif (defaultRegistry) {\n\t\t\t\t\tconst defaultReg = regs.find((r) => r.name === defaultRegistry);\n\t\t\t\t\tif (defaultReg) {\n\t\t\t\t\t\tsetSelectedRegistry(defaultReg);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\ttoast({\n\t\t\t\t\ttitle: \"Error\",\n\t\t\t\t\tdescription: \"Failed to load registries\",\n\t\t\t\t\tvariant: \"destructive\",\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\t\tloadRegistries();\n\t}, [defaultRegistry, toast]);\n\n\t// Load registry items when registry changes\n\tuseEffect(() => {\n\t\tif (selectedRegistry) {\n\t\t\tstartTransition(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst items = await fetchRegistryIndex(selectedRegistry.url);\n\t\t\t\t\tsetRegistryItems(items);\n\t\t\t\t} catch (error) {\n\t\t\t\t\ttoast({\n\t\t\t\t\t\ttitle: \"Error\",\n\t\t\t\t\t\tdescription: \"Failed to load registry items\",\n\t\t\t\t\t\tvariant: \"destructive\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}, [selectedRegistry, toast]);\n\n\t// Check if docs exist when item is selected\n\tuseEffect(() => {\n\t\tif (!selectedItem || !selectedRegistry) return;\n\n\t\tconst checkDocs = async () => {\n\t\t\tsetCheckingDocs(true);\n\t\t\ttry {\n\t\t\t\t// Construct potential docs URLs based on registry\n\t\t\t\tlet url = \"\";\n\t\t\t\tif (selectedRegistry.name === \"Magic UI\") {\n\t\t\t\t\turl = `https://magicui.design/docs/components/${selectedItem.name}`;\n\t\t\t\t} else if (selectedRegistry.name === \"shadcn/ui\") {\n\t\t\t\t\turl = `https://ui.shadcn.com/docs/components/${selectedItem.name}`;\n\t\t\t\t}\n\n\t\t\t\tif (!url) {\n\t\t\t\t\tsetDocsUrl(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Test if the docs exist\n\t\t\t\tconst response = await fetch(url, { method: \"HEAD\" });\n\t\t\t\tsetDocsUrl(response.ok ? url : null);\n\t\t\t} catch (error) {\n\t\t\t\tsetDocsUrl(null);\n\t\t\t} finally {\n\t\t\t\tsetCheckingDocs(false);\n\t\t\t}\n\t\t};\n\n\t\tcheckDocs();\n\t}, [selectedItem, selectedRegistry]);\n\n\t// Load installed components\n\tuseEffect(() => {\n\t\tconst loadInstalledComponents = async () => {\n\t\t\ttry {\n\t\t\t\tconst projectRoot = await getProjectRoot();\n\t\t\t\tconst components = await getInstalledComponents(projectRoot);\n\t\t\t\tsetInstalledComponents(components);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to load installed components:\", error);\n\t\t\t}\n\t\t};\n\n\t\tloadInstalledComponents();\n\t}, []);\n\n\t// Refresh installed components after installation\n\tuseEffect(() => {\n\t\tif (!installing) {\n\t\t\tconst refreshComponents = async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst projectRoot = await getProjectRoot();\n\t\t\t\t\tconst components = await getInstalledComponents(projectRoot);\n\t\t\t\t\tsetInstalledComponents(components);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Failed to refresh installed components:\", error);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\trefreshComponents();\n\t\t}\n\t}, [installing]);\n\n\t// Handle item selection\n\tconst handleItemSelect = useCallback(\n\t\tasync (item: RegistryItem) => {\n\t\t\tsetSelectedItem(item);\n\t\t\tif (!selectedRegistry) return;\n\n\t\t\tstartTransition(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst details = await fetchItemDetails(\n\t\t\t\t\t\titem.type === \"registry:block\"\n\t\t\t\t\t\t\t? selectedRegistry.baseBlockUrl!\n\t\t\t\t\t\t\t: selectedRegistry.baseComponentUrl!,\n\t\t\t\t\t\titem.name\n\t\t\t\t\t);\n\t\t\t\t\tsetItemDetails(details);\n\t\t\t\t} catch (error) {\n\t\t\t\t\ttoast({\n\t\t\t\t\t\ttitle: \"Error\",\n\t\t\t\t\t\tdescription: \"Failed to load item details\",\n\t\t\t\t\t\tvariant: \"destructive\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\t[selectedRegistry, toast]\n\t);\n\n\t// Handle installation\n\tconst handleInstall = useCallback(async () => {\n\t\tif (!selectedItem || !itemDetails) return;\n\n\t\tsetInstalling(true);\n\t\tsetInstallOutput([]);\n\n\t\ttry {\n\t\t\tconst projectRoot = await getProjectRoot();\n\t\t\tconst response = await installComponent(\n\t\t\t\titemDetails.componentUrl ||\n\t\t\t\t`${selectedRegistry?.baseComponentUrl}/styles/default/${selectedItem.name}.json`,\n\t\t\t\tprojectRoot,\n\t\t\t\toverwrite\n\t\t\t);\n\t\t\tconst reader = response.getReader();\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tconst chunk = new TextDecoder().decode(value);\n\t\t\t\tsetInstallOutput((prev) => [...prev, chunk]);\n\t\t\t}\n\n\t\t\ttoast({\n\t\t\t\ttitle: \"Success\",\n\t\t\t\tdescription: `Successfully installed ${selectedItem.name}`,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\ttoast({\n\t\t\t\ttitle: \"Error\",\n\t\t\t\tdescription:\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: \"Failed to install component\",\n\t\t\t\tvariant: \"destructive\",\n\t\t\t});\n\t\t} finally {\n\t\t\tsetInstalling(false);\n\t\t}\n\t}, [selectedItem, itemDetails, overwrite, toast]);\n\n\t// Filter items based on search query and filters\n\tconst filteredItems = useMemo(() => {\n\t\tif (!registryItems?.length) return [];\n\n\t\treturn registryItems.filter((item) => {\n\t\t\t// Filter by type\n\t\t\tif (filterType !== \"all\") {\n\t\t\t\tif (filterType === \"components\" && item.type !== \"registry:ui\")\n\t\t\t\t\treturn false;\n\t\t\t\tif (filterType === \"blocks\" && item.type !== \"registry:block\")\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Filter by category\n\t\t\tif (filterCategory !== \"all\" && item?.category !== filterCategory) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Filter by search query\n\t\t\tif (searchQuery) {\n\t\t\t\tconst query = searchQuery.toLowerCase();\n\t\t\t\treturn (\n\t\t\t\t\titem.name.toLowerCase().includes(query) ||\n\t\t\t\t\titem.description?.toLowerCase().includes(query) ||\n\t\t\t\t\tfalse\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t});\n\t}, [registryItems, filterType, filterCategory, searchQuery]);\n\n\t// Group items by category\n\tconst groupedItems = useMemo(() => {\n\t\tif (!filteredItems?.length) return {};\n\t\treturn categorizeItems(filteredItems);\n\t}, [filteredItems]);\n\n\treturn (\n\t\t<div className=\"grid h-[800px] grid-cols-[300px_1fr] gap-6\">\n\t\t\t{/* Left sidebar */}\n\t\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t\t{/* Registry selector */}\n\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t<Popover open={open} onOpenChange={setOpen}>\n\t\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\t\t\taria-expanded={open}\n\t\t\t\t\t\t\t\tclassName=\"justify-between w-full\"\n\t\t\t\t\t\t\t\tdisabled={!registries.length}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{selectedRegistry?.name ?? \"Select registry...\"}\n\t\t\t\t\t\t\t\t<ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t\t<PopoverContent className=\"p-0\">\n\t\t\t\t\t\t\t<Command>\n\t\t\t\t\t\t\t\t<CommandList>\n\t\t\t\t\t\t\t\t\t<CommandInput placeholder=\"Search registries...\" />\n\t\t\t\t\t\t\t\t\t<CommandEmpty>No registry found.</CommandEmpty>\n\t\t\t\t\t\t\t\t\t<CommandGroup heading=\"Built-in Registries\">\n\t\t\t\t\t\t\t\t\t\t{registries\n\t\t\t\t\t\t\t\t\t\t\t.filter((r) => !r.custom)\n\t\t\t\t\t\t\t\t\t\t\t.map((registry) => (\n\t\t\t\t\t\t\t\t\t\t\t\t<CommandItem\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={registry.name}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetSelectedRegistry(registry);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpen(false);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetSelectedItem(null);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetItemDetails(null);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetRegistryItems([]);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetSearchQuery(\"\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetFilterType(\"all\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetFilterCategory(\"all\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Check\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"mr-2 h-4 w-4\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tselectedRegistry?.name === registry.name\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"opacity-100\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"opacity-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{registry.name}\n\t\t\t\t\t\t\t\t\t\t\t\t</CommandItem>\n\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</CommandGroup>\n\t\t\t\t\t\t\t\t\t<CommandGroup heading=\"Custom Registries\">\n\t\t\t\t\t\t\t\t\t\t{registries\n\t\t\t\t\t\t\t\t\t\t\t.filter((r) => r.custom)\n\t\t\t\t\t\t\t\t\t\t\t.map((registry) => (\n\t\t\t\t\t\t\t\t\t\t\t\t<CommandItem\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={registry.name}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetSelectedRegistry(registry);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpen(false);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetSelectedItem(null);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetItemDetails(null);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetRegistryItems([]);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetSearchQuery(\"\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetFilterType(\"all\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetFilterCategory(\"all\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex justify-between\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Check\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"mr-2 h-4 w-4\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tselectedRegistry?.name === registry.name\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"opacity-100\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"opacity-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{registry.name}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 p-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (selectedRegistry?.name === registry.name) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetSelectedRegistry(null);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tremoveCustomRegistry(registry.name);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst updatedRegistries = registries.filter(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(r) => r.name !== registry.name\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetRegistries(updatedRegistries);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Trash2 className=\"h-4 w-4 text-destructive\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t</CommandItem>\n\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</CommandGroup>\n\t\t\t\t\t\t\t\t</CommandList>\n\t\t\t\t\t\t\t</Command>\n\t\t\t\t\t\t</PopoverContent>\n\t\t\t\t\t</Popover>\n\t\t\t\t\t<AddRegistryDialog\n\t\t\t\t\t\tonAdd={(registry) => {\n\t\t\t\t\t\t\taddCustomRegistry({ ...registry, custom: true });\n\t\t\t\t\t\t\tsetRegistries((prev) => [...prev, { ...registry, custom: true }]);\n\t\t\t\t\t\t\tsetSelectedRegistry({ ...registry, custom: true });\n\t\t\t\t\t\t\tsetOpen(false);\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Search and filters */}\n\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\tplaceholder=\"Search components...\"\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Popover>\n\t\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t\t<Button variant=\"outline\" size=\"icon\">\n\t\t\t\t\t\t\t\t<Filter className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t\t<PopoverContent className=\"w-56\" align=\"end\">\n\t\t\t\t\t\t\t<Command>\n\t\t\t\t\t\t\t\t<CommandList>\n\t\t\t\t\t\t\t\t\t<CommandGroup heading=\"Type\">\n\t\t\t\t\t\t\t\t\t\t<CommandItem onSelect={() => setFilterType(\"all\")}>\n\t\t\t\t\t\t\t\t\t\t\t<Check\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"mr-2 h-4 w-4\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tfilterType === \"all\" ? \"opacity-100\" : \"opacity-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\tAll\n\t\t\t\t\t\t\t\t\t\t</CommandItem>\n\t\t\t\t\t\t\t\t\t\t<CommandItem onSelect={() => setFilterType(\"components\")}>\n\t\t\t\t\t\t\t\t\t\t\t<Check\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"mr-2 h-4 w-4\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tfilterType === \"components\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"opacity-100\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"opacity-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\tComponents\n\t\t\t\t\t\t\t\t\t\t</CommandItem>\n\t\t\t\t\t\t\t\t\t\t<CommandItem onSelect={() => setFilterType(\"blocks\")}>\n\t\t\t\t\t\t\t\t\t\t\t<Check\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"mr-2 h-4 w-4\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tfilterType === \"blocks\" ? \"opacity-100\" : \"opacity-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\tBlocks\n\t\t\t\t\t\t\t\t\t\t</CommandItem>\n\t\t\t\t\t\t\t\t\t</CommandGroup>\n\t\t\t\t\t\t\t\t</CommandList>\n\t\t\t\t\t\t\t</Command>\n\t\t\t\t\t\t</PopoverContent>\n\t\t\t\t\t</Popover>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Component list */}\n\t\t\t\t<div className=\"flex-1 overflow-hidden\">\n\t\t\t\t\t<ScrollArea className=\"h-full\">\n\t\t\t\t\t\t{Object.entries(groupedItems).map(([category, items]) => (\n\t\t\t\t\t\t\t<div key={category} className=\"mb-4\">\n\t\t\t\t\t\t\t\t<h3 className=\"mb-2 text-sm font-semibold\">{category}</h3>\n\t\t\t\t\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t\t\t\t\t{items.map((item) => (\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\tkey={item.name}\n\t\t\t\t\t\t\t\t\t\t\tvariant={\n\t\t\t\t\t\t\t\t\t\t\t\tselectedItem?.name === item.name ? \"secondary\" : \"ghost\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"w-full justify-start\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => handleItemSelect(item)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center justify-between w-full\">\n\t\t\t\t\t\t\t\t\t\t\t\t<span>{item.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{installedComponents.includes(item.name) && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TooltipProvider delayDuration={0}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Check className=\"h-4 w-4 text-green-500\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p>Installed</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</TooltipProvider>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ScrollArea>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Right content */}\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t{itemDetails ? (\n\t\t\t\t\t<div className=\"space-y-6\">\n\t\t\t\t\t\t{/* Title and Install Button */}\n\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t\t\t\t<h3 className=\"text-lg font-semibold\">{itemDetails.name}</h3>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{itemDetails.description}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<CopyButton\n\t\t\t\t\t\t\t\t\t\tvalue={\n\t\t\t\t\t\t\t\t\t\t\titemDetails.componentUrl ||\n\t\t\t\t\t\t\t\t\t\t\t`${selectedRegistry?.baseComponentUrl}/styles/default/${selectedItem?.name}.json`\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\ttooltip=\"Copy JSON URL\"\n\t\t\t\t\t\t\t\t\t\ticon={Link}\n\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<span className=\"sr-only text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\tJSON URL\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<CopyButton\n\t\t\t\t\t\t\t\t\t\tvalue={`npx shadcn@latest add \"${itemDetails.componentUrl ||\n\t\t\t\t\t\t\t\t\t\t\t`${selectedRegistry?.baseComponentUrl}/styles/default/${selectedItem?.name}.json`\n\t\t\t\t\t\t\t\t\t\t\t}\"`}\n\t\t\t\t\t\t\t\t\t\ttooltip=\"Copy Install Command\"\n\t\t\t\t\t\t\t\t\t\ticon={CodeIcon}\n\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<span className=\"sr-only text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\tInstall Command\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{itemDetails.files?.some((f) => f.content) && (\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<CopyButton\n\t\t\t\t\t\t\t\t\t\t\tvalue={itemDetails.files\n\t\t\t\t\t\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t\t\t\t\t\t(f) => f.content && !f.path.endsWith(\".test.tsx\")\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t.map((f) => f.content)\n\t\t\t\t\t\t\t\t\t\t\t\t.join(\"\\n\\n\")}\n\t\t\t\t\t\t\t\t\t\t\ttooltip=\"Copy Component Code\"\n\t\t\t\t\t\t\t\t\t\t\ticon={FileCode}\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\tCode\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{docsUrl && (\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<TooltipProvider delayDuration={0}>\n\t\t\t\t\t\t\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tasChild\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thref={docsUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<BookOpen className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tView Documentation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t\t\t\t\t\t<TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p>View Documentation</p>\n\t\t\t\t\t\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t\t\t\t\t\t</TooltipProvider>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t\t\t\t\t{!installing && (\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<Switch\n\t\t\t\t\t\t\t\t\t\t\t\tid=\"overwrite\"\n\t\t\t\t\t\t\t\t\t\t\t\tchecked={overwrite}\n\t\t\t\t\t\t\t\t\t\t\t\tonCheckedChange={setOverwrite}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\t\thtmlFor=\"overwrite\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-sm text-muted-foreground\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tOverwrite existing\n\t\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tonClick={handleInstall}\n\t\t\t\t\t\t\t\t\t\tdisabled={installing}\n\t\t\t\t\t\t\t\t\t\tvariant={\n\t\t\t\t\t\t\t\t\t\t\tinstalledComponents.includes(selectedItem?.name || \"\")\n\t\t\t\t\t\t\t\t\t\t\t\t? \"secondary\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"default\"\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{installing ? (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t<Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n\t\t\t\t\t\t\t\t\t\t\t\tInstalling...\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t) : installedComponents.includes(\n\t\t\t\t\t\t\t\t\t\t\tselectedItem?.name || \"\"\n\t\t\t\t\t\t\t\t\t\t) ? (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t<Download className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t\t\tReinstall\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t<Download className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t\t\tInstall\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Preview */}\n\t\t\t\t\t\t{itemDetails.type === \"registry:block\" ? (\n\t\t\t\t\t\t\t<BlockPreview block={itemDetails} />\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<Preview item={itemDetails} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Component Stats */}\n\t\t\t\t\t\t<ComponentStats selectedItem={itemDetails} />\n\n\t\t\t\t\t\t{/* File tree */}\n\t\t\t\t\t\t{itemDetails.files && (\n\t\t\t\t\t\t\t<Card className=\"p-4\">\n\t\t\t\t\t\t\t\t<h3 className=\"mb-4 text-lg font-semibold\">Files</h3>\n\t\t\t\t\t\t\t\t<FileTree\n\t\t\t\t\t\t\t\t\tfiles={itemDetails.files}\n\t\t\t\t\t\t\t\t\tselectedFile={selectedFile}\n\t\t\t\t\t\t\t\t\tonFileSelect={(file) => setSelectedFile(file.path)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</Card>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"flex h-full items-center justify-center\">\n\t\t\t\t\t\t<p className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tSelect a component to view details\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Installation output overlay */}\n\t\t\t{installOutput.length > 0 && (\n\t\t\t\t<div className=\"fixed bottom-4 right-4 w-[500px] animate-in slide-in-from-bottom duration-300\">\n\t\t\t\t\t<Card className=\"shadow-2xl border-black/10 bg-[#1E1E1E]\">\n\t\t\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t\t\t<div className=\"absolute top-0 left-0 right-0 h-8 bg-[#323233] rounded-t-lg flex items-center justify-between px-3\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t<div className=\"w-2.5 h-2.5 rounded-full bg-[#FF5F57]\" />\n\t\t\t\t\t\t\t\t\t<div className=\"w-2.5 h-2.5 rounded-full bg-[#FEBC2E]\" />\n\t\t\t\t\t\t\t\t\t<div className=\"w-2.5 h-2.5 rounded-full bg-[#28C840]\" />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"absolute inset-0 flex items-center justify-center\">\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-zinc-400 font-medium\">\n\t\t\t\t\t\t\t\t\t\tInstallation Output\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tclassName=\"relative z-10 h-6 w-6 p-0 text-zinc-400 hover:text-zinc-300\"\n\t\t\t\t\t\t\t\t\tonClick={() => setInstallOutput([])}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"pt-8\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center px-4 py-2 bg-[#252526]\">\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t\t\t\t\t\t\t{installing ? (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 px-2 py-1 bg-blue-500/10 text-blue-400 rounded-full\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Loader2 className=\"h-3 w-3 animate-spin\" />\n\t\t\t\t\t\t\t\t\t\t\t\t\tInstalling...\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 px-2 py-1 bg-emerald-500/10 text-emerald-400 rounded-full\">\n\t\t\t\t\t\t\t\t\t\t\t\t<Check className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\t\t\t\tComplete\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<TerminalComponent\n\t\t\t\t\t\t\t\t\toutput={installOutput}\n\t\t\t\t\t\t\t\t\tclassName=\"h-[300px] rounded-b-lg\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Card>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/registry-browser.tsx"
    },
    {
      "path": "_components/add-registry-dialog.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/registry/default/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/registry/default/components/ui/dialog\";\nimport { Input } from \"@/registry/default/components/ui/input\";\nimport { Label } from \"@/registry/default/components/ui/label\";\nimport { Registry } from \"@/registry/default/lib/types\";\nimport { PlusCircle } from \"lucide-react\";\nimport { useState } from \"react\";\n\ninterface AddRegistryDialogProps {\n  onAdd: (registry: Registry) => void;\n}\n\nexport function AddRegistryDialog({ onAdd }: AddRegistryDialogProps) {\n  const [open, setOpen] = useState(false);\n  const [name, setName] = useState(\"\");\n  const [url, setUrl] = useState(\"\");\n  const [description, setDescription] = useState(\"\");\n\n  const handleSubmit = (e: React.FormEvent) => {\n    e.preventDefault();\n    onAdd({\n      name,\n      url,\n      description,\n      baseComponentUrl: url,\n      baseBlockUrl: url,\n    });\n    setOpen(false);\n    setName(\"\");\n    setUrl(\"\");\n    setDescription(\"\");\n  };\n\n  return (\n    <Dialog open={open} onOpenChange={setOpen}>\n      <DialogTrigger asChild>\n        <Button variant=\"outline\" size=\"sm\" className=\"w-full\">\n          <PlusCircle className=\"mr-2 h-4 w-4\" />\n          Add Registry\n        </Button>\n      </DialogTrigger>\n      <DialogContent>\n        <DialogHeader>\n          <DialogTitle>Add Custom Registry</DialogTitle>\n          <DialogDescription>\n            Add a custom component registry to browse and install components\n            from.\n          </DialogDescription>\n        </DialogHeader>\n        <form onSubmit={handleSubmit}>\n          <div className=\"grid gap-4 py-4\">\n            <div className=\"grid gap-2\">\n              <Label htmlFor=\"name\">Name</Label>\n              <Input\n                id=\"name\"\n                value={name}\n                onChange={(e) => setName(e.target.value)}\n                placeholder=\"My Custom Registry\"\n                required\n              />\n            </div>\n            <div className=\"grid gap-2\">\n              <Label htmlFor=\"url\">Registry URL</Label>\n              <Input\n                id=\"url\"\n                value={url}\n                onChange={(e) => setUrl(e.target.value)}\n                placeholder=\"https://example.com/registry\"\n                required\n              />\n            </div>\n          </div>\n          <DialogFooter>\n            <Button type=\"submit\">Add Registry</Button>\n          </DialogFooter>\n        </form>\n      </DialogContent>\n    </Dialog>\n  );\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/add-registry-dialog.tsx"
    },
    {
      "path": "_components/block-preview.tsx",
      "content": "\"use client\";\n\nimport { Card } from \"@/registry/default/components/ui/card\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/registry/default/components/ui/tabs\";\nimport { type RegistryItem } from \"@/registry/default/lib/types\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport { Loader2 } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\ninterface BlockPreviewProps {\n  block: RegistryItem;\n  className?: string;\n}\n\ninterface BlockConfig {\n  name: string;\n  description?: string;\n  dependencies?: string[];\n  props?: Record<string, any>;\n  layout?: {\n    header?: boolean;\n    footer?: boolean;\n    sidebar?: boolean;\n  };\n}\n\nfunction loadBlockConfig(files: RegistryItem[\"files\"]): BlockConfig | null {\n  const configFile = files.find((f) => f.path.endsWith(\"block.config.json\"));\n  if (!configFile?.content) return null;\n\n  try {\n    return JSON.parse(configFile.content);\n  } catch {\n    return null;\n  }\n}\n\nfunction createBlockPreview(\n  block: RegistryItem,\n  config: BlockConfig | null\n): string {\n  const mainFile = block.files.find(\n    (f) =>\n      f.path.endsWith(\"page.tsx\") ||\n      f.path.endsWith(\"index.tsx\") ||\n      f.path.endsWith(\".tsx\")\n  );\n\n  if (!mainFile?.content) {\n    throw new Error(\"No main file found\");\n  }\n\n  // Extract styles\n  const styles = block.files\n    .filter((f) => f.path.endsWith(\".css\"))\n    .map((f) => f.content || \"\")\n    .join(\"\\n\");\n\n  // Create HTML with layout\n  const html = `\n    <!DOCTYPE html>\n    <html>\n      <head>\n        <meta charset=\"utf-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        <title>${block.name} Preview</title>\n        <script src=\"https://unpkg.com/react@18/umd/react.development.js\"></script>\n        <script src=\"https://unpkg.com/react-dom@18/umd/react-dom.development.js\"></script>\n        <link href=\"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css\" rel=\"stylesheet\">\n        <style>\n          ${styles}\n          /* Layout styles */\n          .layout {\n            display: grid;\n            min-height: 100vh;\n            grid-template-areas:\n              ${config?.layout?.header ? '\"header header header\"' : \"\"}\n              ${\n                config?.layout?.sidebar\n                  ? '\"sidebar main main\"'\n                  : '\"main main main\"'\n              }\n              ${config?.layout?.footer ? '\"footer footer footer\"' : \"\"};\n            grid-template-rows: ${config?.layout?.header ? \"auto\" : \"\"} 1fr ${\n    config?.layout?.footer ? \"auto\" : \"\"\n  };\n            grid-template-columns: ${\n              config?.layout?.sidebar ? \"250px\" : \"\"\n            } 1fr 1fr;\n          }\n          .header { grid-area: header; }\n          .sidebar { grid-area: sidebar; }\n          .main { grid-area: main; }\n          .footer { grid-area: footer; }\n        </style>\n      </head>\n      <body>\n        <div class=\"layout\">\n          ${\n            config?.layout?.header\n              ? '<header class=\"header bg-gray-100 p-4\">Header</header>'\n              : \"\"\n          }\n          ${\n            config?.layout?.sidebar\n              ? '<aside class=\"sidebar bg-gray-50 p-4\">Sidebar</aside>'\n              : \"\"\n          }\n          <main class=\"main p-4\">\n            <div id=\"root\"></div>\n          </main>\n          ${\n            config?.layout?.footer\n              ? '<footer class=\"footer bg-gray-100 p-4\">Footer</footer>'\n              : \"\"\n          }\n        </div>\n        <script type=\"module\">\n          ${\n            mainFile.content\n              .replace(/import[^;]+;/g, \"\") // Remove imports\n              .replace(\"export default\", \"window.BlockComponent =\") // Export component\n          }\n          const root = document.getElementById('root');\n          const props = ${JSON.stringify(config?.props || {})};\n          if (typeof BlockComponent === 'function') {\n            root.innerHTML = BlockComponent(props);\n          }\n        </script>\n      </body>\n    </html>\n  `;\n\n  const blob = new Blob([html], { type: \"text/html\" });\n  return URL.createObjectURL(blob);\n}\n\nexport function BlockPreview({ block, className }: BlockPreviewProps) {\n  const [previewUrl, setPreviewUrl] = useState<string>(\"\");\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n  const [config, setConfig] = useState<BlockConfig | null>(null);\n\n  useEffect(() => {\n    try {\n      const blockConfig = loadBlockConfig(block.files);\n      setConfig(blockConfig);\n      const url = createBlockPreview(block, blockConfig);\n      setPreviewUrl(url);\n      setLoading(false);\n      setError(null);\n\n      return () => {\n        URL.revokeObjectURL(url);\n      };\n    } catch (err) {\n      setError(err instanceof Error ? err.message : \"Failed to create preview\");\n      setLoading(false);\n    }\n  }, [block]);\n\n  return (\n    <Card className={cn(\"overflow-hidden\", className)}>\n      <Tabs defaultValue=\"desktop\">\n        <div className=\"border-b px-4\">\n          <TabsList className=\"h-12\">\n            <TabsTrigger value=\"desktop\">Desktop</TabsTrigger>\n            <TabsTrigger value=\"tablet\">Tablet</TabsTrigger>\n            <TabsTrigger value=\"mobile\">Mobile</TabsTrigger>\n          </TabsList>\n        </div>\n        <TabsContent value=\"desktop\" className=\"border-none p-0\">\n          {loading ? (\n            <div className=\"flex h-[600px] items-center justify-center\">\n              <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n            </div>\n          ) : error ? (\n            <div className=\"flex h-[600px] items-center justify-center text-destructive\">\n              {error}\n            </div>\n          ) : (\n            <iframe\n              src={previewUrl}\n              className=\"h-[600px] w-full\"\n              sandbox=\"allow-scripts\"\n              title=\"Desktop Preview\"\n            />\n          )}\n        </TabsContent>\n        <TabsContent value=\"tablet\" className=\"border-none p-0\">\n          <div className=\"flex justify-center p-4\">\n            <iframe\n              src={previewUrl}\n              className=\"h-[800px] w-[768px]\"\n              sandbox=\"allow-scripts\"\n              title=\"Tablet Preview\"\n            />\n          </div>\n        </TabsContent>\n        <TabsContent value=\"mobile\" className=\"border-none p-0\">\n          <div className=\"flex justify-center p-4\">\n            <iframe\n              src={previewUrl}\n              className=\"h-[800px] w-[375px]\"\n              sandbox=\"allow-scripts\"\n              title=\"Mobile Preview\"\n            />\n          </div>\n        </TabsContent>\n      </Tabs>\n    </Card>\n  );\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/block-preview.tsx"
    },
    {
      "path": "_components/component-stats.tsx",
      "content": "\"use client\";\n\nimport { Card } from \"@/registry/default/components/ui/card\";\nimport { Progress } from \"@/registry/default/components/ui/progress\";\nimport { type RegistryItem } from \"@/registry/default/lib/types\";\nimport { useEffect, useState } from \"react\";\nimport {\n\tgetDependencies,\n\tgetInstalledComponents,\n\tgetProjectRoot,\n} from \"../actions\";\n\ninterface ComponentStatsProps {\n\tselectedItem: RegistryItem | null;\n}\n\ninterface DependencyStats {\n\ttotal: number;\n\tinstalled: number;\n\tmissing: string[];\n}\n\nexport function ComponentStats({ selectedItem }: ComponentStatsProps) {\n\tconst [stats, setStats] = useState<{\n\t\tdependencies: DependencyStats;\n\t\tregistryDependencies: DependencyStats;\n\t\tinstalledComponents: string[];\n\t} | null>(null);\n\n\tuseEffect(() => {\n\t\tconst loadStats = async () => {\n\t\t\tif (!selectedItem) return;\n\n\t\t\ttry {\n\t\t\t\tconst [deps, components] = await Promise.all([\n\t\t\t\t\tgetDependencies(await getProjectRoot()),\n\t\t\t\t\tgetInstalledComponents(await getProjectRoot()),\n\t\t\t\t]);\n\n\t\t\t\tconst allDeps = { ...deps.dependencies, ...deps.devDependencies };\n\n\t\t\t\t// Check dependencies\n\t\t\t\tconst dependencies: DependencyStats = {\n\t\t\t\t\ttotal: selectedItem.dependencies?.length || 0,\n\t\t\t\t\tinstalled: 0,\n\t\t\t\t\tmissing: [],\n\t\t\t\t};\n\n\t\t\t\tselectedItem.dependencies?.forEach((dep) => {\n\t\t\t\t\tconst [name] = dep.split(\"@\");\n\t\t\t\t\tif (name in allDeps) {\n\t\t\t\t\t\tdependencies.installed++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdependencies.missing.push(dep);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// Check registry dependencies\n\t\t\t\tconst registryDependencies: DependencyStats = {\n\t\t\t\t\ttotal: selectedItem.registryDependencies?.length || 0,\n\t\t\t\t\tinstalled: 0,\n\t\t\t\t\tmissing: [],\n\t\t\t\t};\n\n\t\t\t\tselectedItem.registryDependencies?.forEach((dep) => {\n\t\t\t\t\tif (components.includes(dep)) {\n\t\t\t\t\t\tregistryDependencies.installed++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tregistryDependencies.missing.push(dep);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tsetStats({\n\t\t\t\t\tdependencies,\n\t\t\t\t\tregistryDependencies,\n\t\t\t\t\tinstalledComponents: components,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to load stats:\", error);\n\t\t\t}\n\t\t};\n\n\t\tloadStats();\n\t}, [selectedItem]);\n\n\tif (!selectedItem || !stats) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<div className=\"grid gap-4 md:grid-cols-2\">\n\t\t\t{/* Dependencies */}\n\t\t\t<Card className=\"p-4\">\n\t\t\t\t<h3 className=\"font-semibold\">Dependencies</h3>\n\t\t\t\t<div className=\"mt-2\">\n\t\t\t\t\t<div className=\"mb-2 flex items-center justify-between text-sm\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{stats.dependencies.installed} / {stats.dependencies.total}{\" \"}\n\t\t\t\t\t\t\tinstalled\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t{stats.dependencies.total\n\t\t\t\t\t\t\t\t? Math.round(\n\t\t\t\t\t\t\t\t\t(stats.dependencies.installed / stats.dependencies.total) *\n\t\t\t\t\t\t\t\t\t100\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: 100}\n\t\t\t\t\t\t\t%\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Progress\n\t\t\t\t\t\tvalue={\n\t\t\t\t\t\t\t(stats.dependencies.installed / stats.dependencies.total) * 100\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t{stats.dependencies.missing.length > 0 && (\n\t\t\t\t\t<div className=\"mt-4\">\n\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\tMissing dependencies:\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<div className=\"mt-1 space-x-1\">\n\t\t\t\t\t\t\t{stats.dependencies.missing.map((dep) => (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tkey={dep}\n\t\t\t\t\t\t\t\t\tclassName=\"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold text-destructive\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{dep}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</Card>\n\n\t\t\t{/* Registry Dependencies */}\n\t\t\t<Card className=\"p-4\">\n\t\t\t\t<h3 className=\"font-semibold\">Registry Dependencies</h3>\n\t\t\t\t<div className=\"mt-2\">\n\t\t\t\t\t<div className=\"mb-2 flex items-center justify-between text-sm\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{stats.registryDependencies.installed} /{\" \"}\n\t\t\t\t\t\t\t{stats.registryDependencies.total} installed\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t{stats.registryDependencies.total\n\t\t\t\t\t\t\t\t? Math.round(\n\t\t\t\t\t\t\t\t\t(stats.registryDependencies.installed /\n\t\t\t\t\t\t\t\t\t\tstats.registryDependencies.total) *\n\t\t\t\t\t\t\t\t\t100\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: 100}\n\t\t\t\t\t\t\t%\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Progress\n\t\t\t\t\t\tvalue={\n\t\t\t\t\t\t\t(stats.registryDependencies.installed /\n\t\t\t\t\t\t\t\tstats.registryDependencies.total) *\n\t\t\t\t\t\t\t100\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t{stats.registryDependencies.missing.length > 0 && (\n\t\t\t\t\t<div className=\"mt-4\">\n\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">Missing components:</p>\n\t\t\t\t\t\t<div className=\"mt-1 space-x-1\">\n\t\t\t\t\t\t\t{stats.registryDependencies.missing.map((dep) => (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tkey={dep}\n\t\t\t\t\t\t\t\t\tclassName=\"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold text-destructive\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{dep}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</Card>\n\t\t</div>\n\t);\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/component-stats.tsx"
    },
    {
      "path": "_components/copy-button.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/registry/default/components/ui/button\";\nimport {\n\tTooltip,\n\tTooltipContent,\n\tTooltipProvider,\n\tTooltipTrigger,\n} from \"@/registry/default/components/ui/tooltip\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport { Check, Copy, LucideIcon } from \"lucide-react\";\nimport { useState } from \"react\";\n\ninterface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {\n\tvalue: string;\n\ttooltip?: string;\n\ticon?: LucideIcon;\n\tclassName?: string;\n\tvariant?: \"outline\" | \"ghost\";\n}\n\nexport function CopyButton({\n\tvalue,\n\ttooltip,\n\ticon: Icon = Copy,\n\tclassName,\n\tvariant = \"ghost\",\n\t...props\n}: CopyButtonProps) {\n\tconst [hasCopied, setHasCopied] = useState(false);\n\n\tasync function copyToClipboard() {\n\t\ttry {\n\t\t\tawait navigator.clipboard.writeText(value);\n\t\t\tsetHasCopied(true);\n\t\t\tsetTimeout(() => setHasCopied(false), 2000);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to copy text:\", error);\n\t\t}\n\t}\n\n\tconst button = (\n\t\t<Button\n\t\t\tsize=\"icon\"\n\t\t\tvariant={variant}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative h-8 w-8 text-muted-foreground hover:bg-muted\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={copyToClipboard}\n\t\t\t{...props}\n\t\t>\n\t\t\t{hasCopied ? <Check className=\"h-4 w-4\" /> : <Icon className=\"h-4 w-4\" />}\n\t\t\t<span className=\"sr-only\">Copy</span>\n\t\t</Button>\n\t);\n\n\tif (!tooltip) return button;\n\n\treturn (\n\t\t<TooltipProvider delayDuration={0}>\n\t\t\t<Tooltip>\n\t\t\t\t<TooltipTrigger asChild>{button}</TooltipTrigger>\n\t\t\t\t<TooltipContent>\n\t\t\t\t\t<p>{tooltip}</p>\n\t\t\t\t</TooltipContent>\n\t\t\t</Tooltip>\n\t\t</TooltipProvider>\n\t);\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/copy-button.tsx"
    },
    {
      "path": "_components/file-tree.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/registry/default/components/ui/button\";\nimport { ScrollArea } from \"@/registry/default/components/ui/scroll-area\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport { ChevronDown, ChevronRight, File, Folder } from \"lucide-react\";\nimport { useState } from \"react\";\n\ninterface FileTreeProps {\n  files: {\n    path: string;\n    type: string;\n    content?: string;\n  }[];\n  onFileSelect?: (file: { path: string; content?: string }) => void;\n  selectedFile?: string;\n}\n\ninterface TreeNode {\n  name: string;\n  path: string;\n  type: \"file\" | \"directory\";\n  content?: string;\n  children: Record<string, TreeNode>;\n}\n\nfunction buildTree(files: FileTreeProps[\"files\"]): TreeNode {\n  const root: TreeNode = {\n    name: \"root\",\n    path: \"\",\n    type: \"directory\",\n    children: {},\n  };\n\n  files.forEach((file) => {\n    const parts = file.path.split(\"/\");\n    let current = root;\n\n    parts.forEach((part, i) => {\n      const path = parts.slice(0, i + 1).join(\"/\");\n      if (!current.children[part]) {\n        current.children[part] = {\n          name: part,\n          path,\n          type: i === parts.length - 1 ? \"file\" : \"directory\",\n          content: i === parts.length - 1 ? file.content : undefined,\n          children: {},\n        };\n      }\n      current = current.children[part];\n    });\n  });\n\n  return root;\n}\n\nfunction TreeNode({\n  node,\n  level = 0,\n  expanded,\n  onToggle,\n  onFileSelect,\n  selectedFile,\n}: {\n  node: TreeNode;\n  level?: number;\n  expanded: Record<string, boolean>;\n  onToggle: (path: string) => void;\n  onFileSelect?: FileTreeProps[\"onFileSelect\"];\n  selectedFile?: string;\n}) {\n  const isExpanded = expanded[node.path];\n  const hasChildren = Object.keys(node.children).length > 0;\n\n  return (\n    <div>\n      <Button\n        variant=\"ghost\"\n        size=\"sm\"\n        className={cn(\n          \"w-full justify-start\",\n          selectedFile === node.path && \"bg-muted\"\n        )}\n        onClick={() => {\n          if (node.type === \"directory\") {\n            onToggle(node.path);\n          } else if (onFileSelect) {\n            onFileSelect({\n              path: node.path,\n              content: node.content,\n            });\n          }\n        }}\n      >\n        <span\n          style={{ marginLeft: `${level * 12}px` }}\n          className=\"flex items-center\"\n        >\n          {node.type === \"directory\" ? (\n            <>\n              {hasChildren ? (\n                isExpanded ? (\n                  <ChevronDown className=\"mr-2 h-4 w-4\" />\n                ) : (\n                  <ChevronRight className=\"mr-2 h-4 w-4\" />\n                )\n              ) : (\n                <span className=\"mr-2 w-4\" />\n              )}\n              <Folder className=\"mr-2 h-4 w-4\" />\n            </>\n          ) : (\n            <>\n              <span className=\"mr-2 w-4\" />\n              <File className=\"mr-2 h-4 w-4\" />\n            </>\n          )}\n          {node.name}\n        </span>\n      </Button>\n      {isExpanded && hasChildren && (\n        <div>\n          {Object.values(node.children)\n            .sort((a, b) => {\n              // Directories first, then files\n              if (a.type !== b.type) {\n                return a.type === \"directory\" ? -1 : 1;\n              }\n              return a.name.localeCompare(b.name);\n            })\n            .map((child) => (\n              <TreeNode\n                key={child.path}\n                node={child}\n                level={level + 1}\n                expanded={expanded}\n                onToggle={onToggle}\n                onFileSelect={onFileSelect}\n                selectedFile={selectedFile}\n              />\n            ))}\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport function FileTree({ files, onFileSelect, selectedFile }: FileTreeProps) {\n  const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n  const tree = buildTree(files);\n\n  const handleToggle = (path: string) => {\n    setExpanded((prev) => ({\n      ...prev,\n      [path]: !prev[path],\n    }));\n  };\n\n  return (\n    <ScrollArea className=\"min-h-[100px]\">\n      <div className=\"p-2\">\n        <TreeNode\n          node={tree}\n          expanded={expanded}\n          onToggle={handleToggle}\n          onFileSelect={onFileSelect}\n          selectedFile={selectedFile}\n        />\n      </div>\n    </ScrollArea>\n  );\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/file-tree.tsx"
    },
    {
      "path": "_components/preview.tsx",
      "content": "\"use client\";\n\nimport { Card } from \"@/registry/default/components/ui/card\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/registry/default/components/ui/tabs\";\nimport { type RegistryItem } from \"@/registry/default/lib/types\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport { Loader2 } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\ninterface PreviewProps {\n  item: RegistryItem;\n  className?: string;\n}\n\ninterface IframeSandbox {\n  html: string;\n  css: string;\n  js: string;\n}\n\nfunction extractCode(files: RegistryItem[\"files\"]): IframeSandbox {\n  const sandbox: IframeSandbox = {\n    html: \"\",\n    css: \"\",\n    js: \"\",\n  };\n\n  files.forEach((file) => {\n    if (!file.content) return;\n\n    if (file.path.endsWith(\".tsx\") || file.path.endsWith(\".jsx\")) {\n      // Extract JSX/TSX content\n      const jsxContent = file.content\n        // Remove all import statements\n        .replace(/import\\s+[^;]+;?\\s*/g, \"\")\n        // Remove export statements but keep the content\n        .replace(/export\\s+default\\s+/, \"\")\n        .replace(/export\\s+/, \"\")\n        // Remove type annotations\n        .replace(/:\\s*[A-Za-z<>[\\]{}|&]+/g, \"\")\n        .replace(/React\\./g, \"\");\n\n      // Convert JSX to JS (basic conversion for preview)\n      sandbox.js += `\n        try {\n          // Wrap in IIFE to avoid scope issues\n          (function() {\n            console.log('Executing component code');\n\n            // Setup React globals\n            const React = window.React;\n            const ReactDOM = window.ReactDOM;\n            const useState = React.useState;\n            const useEffect = React.useEffect;\n            const useRef = React.useRef;\n\n            // Define component\n            ${jsxContent}\n\n            // Get the component name from the file name\n            const componentName = '${file.path\n              .split(\"/\")\n              .pop()\n              ?.replace(/\\.[jt]sx$/, \"\")}';\n            console.log('Component name:', componentName);\n\n            // Try to find the component function\n            let Component =\n              // Try function declaration\n              (typeof ${\n                jsxContent.match(/function\\s+(\\w+)/)?.[1] || \"null\"\n              } === 'function'\n                ? ${jsxContent.match(/function\\s+(\\w+)/)?.[1]}\n              // Try const/let/var declaration\n              : typeof ${\n                jsxContent.match(/(?:const|let|var)\\s+(\\w+)\\s*=/)?.[1] || \"null\"\n              } === 'function'\n                ? ${jsxContent.match(/(?:const|let|var)\\s+(\\w+)\\s*=/)?.[1]}\n              // Try file name\n              : typeof ${file.path\n                .split(\"/\")\n                .pop()\n                ?.replace(/\\.[jt]sx$/, \"\")} === 'function'\n                ? ${file.path\n                  .split(\"/\")\n                  .pop()\n                  ?.replace(/\\.[jt]sx$/, \"\")}\n              : null);\n\n            console.log('Found component:', Component ? 'yes' : 'no');\n\n            if (Component) {\n              const root = document.getElementById('root');\n              // Use ReactDOM for proper React rendering\n              ReactDOM.render(React.createElement(Component), root);\n              console.log('Component rendered');\n            } else {\n              console.error('Could not find component function');\n              document.getElementById('root').innerHTML = '<div class=\"error\">Could not find component function</div>';\n            }\n          })();\n        } catch (err) {\n          console.error('Error in component:', err);\n          document.getElementById('root').innerHTML = \\`\n            <div class=\"error\">\n              <strong>Error in component:</strong><br>\n              \\${err.message}\n            </div>\n          \\`;\n        }\n      `;\n    } else if (file.path.endsWith(\".css\")) {\n      sandbox.css += file.content;\n    }\n  });\n\n  return sandbox;\n}\n\nfunction createPreviewUrl(item: RegistryItem): string {\n  const sandbox = extractCode(item.files);\n\n  // Add polyfills and dependencies\n  const dependencies = {\n    react: \"https://unpkg.com/react@18/umd/react.development.js\",\n    \"react-dom\": \"https://unpkg.com/react-dom@18/umd/react-dom.development.js\",\n    motion: \"https://unpkg.com/@motionone/dom@10.16.2/dist/motion.min.js\",\n    tailwindcss: \"https://cdn.tailwindcss.com\",\n  };\n\n  const html = `\n    <!DOCTYPE html>\n    <html>\n      <head>\n        <meta charset=\"utf-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        <title>Component Preview</title>\n\n        <!-- Dependencies -->\n        ${Object.entries(dependencies)\n          .filter(([dep]) => !item.dependencies?.some((d) => d.startsWith(dep)))\n          .map(([, url]) => `<script src=\"${url}\"></script>`)\n          .join(\"\\n\")}\n\n        <!-- Tailwind -->\n        <script>\n          window.tailwind = window.tailwind || {};\n          window.tailwind.config = {\n            theme: {\n              extend: ${JSON.stringify(\n                item?.tailwind?.config?.theme?.extend || {}\n              )}\n            }\n          }\n        </script>\n\n        <style>\n          ${sandbox.css}\n          /* Add default styles */\n          body {\n            margin: 0;\n            padding: 1rem;\n            font-family: system-ui, sans-serif;\n          }\n          #root {\n            min-height: 100px;\n            background: #fff;\n          }\n          .error {\n            color: #ef4444;\n            padding: 1rem;\n            border: 1px solid #ef4444;\n            border-radius: 0.5rem;\n            margin: 1rem;\n          }\n        </style>\n      </head>\n      <body>\n        <div id=\"root\"></div>\n        <script>\n          // Add error boundary\n          window.onerror = function(msg, url, line, col, error) {\n            console.error('Error:', msg, url, line, col, error);\n            document.getElementById('root').innerHTML = \\`\n              <div class=\"error\">\n                <strong>Error:</strong> \\${msg}<br>\n                <small>Line \\${line}, Column \\${col}</small>\n              </div>\n            \\`;\n            return false;\n          };\n\n          try {\n            // Log for debugging\n            console.log('Running preview script');\n\n            ${sandbox.js}\n\n            console.log('Preview script completed');\n          } catch (err) {\n            console.error('Error in preview script:', err);\n            document.getElementById('root').innerHTML = \\`\n              <div class=\"error\">\n                <strong>Error:</strong> \\${err.message}\n              </div>\n            \\`;\n          }\n        </script>\n      </body>\n    </html>\n  `;\n\n  const blob = new Blob([html], { type: \"text/html\" });\n  return URL.createObjectURL(blob);\n}\n\nexport function Preview({ item, className }: PreviewProps) {\n  const [previewUrl, setPreviewUrl] = useState<string>(\"\");\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n\n  useEffect(() => {\n    try {\n      const url = createPreviewUrl(item);\n      setPreviewUrl(url);\n      setLoading(false);\n      setError(null);\n\n      return () => {\n        URL.revokeObjectURL(url);\n      };\n    } catch (err) {\n      setError(err instanceof Error ? err.message : \"Failed to create preview\");\n      setLoading(false);\n    }\n  }, [item]);\n\n  return (\n    <Card className={cn(\"overflow-hidden\", className)}>\n      <Tabs defaultValue=\"preview\">\n        <div className=\"border-b p-4\">\n          <TabsList className=\"h-12\">\n            <TabsTrigger value=\"preview\">Preview</TabsTrigger>\n            <TabsTrigger value=\"responsive\">Responsive</TabsTrigger>\n          </TabsList>\n        </div>\n        <TabsContent value=\"preview\" className=\"p-4\">\n          {loading ? (\n            <div className=\"flex h-[400px] items-center justify-center\">\n              <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n            </div>\n          ) : error ? (\n            <div className=\"flex h-[400px] items-center justify-center text-destructive\">\n              {error}\n            </div>\n          ) : (\n            <div className=\"rounded-lg border\">\n              <iframe\n                src={previewUrl}\n                className=\"h-[400px] w-full\"\n                sandbox=\"allow-scripts allow-modals allow-popups allow-same-origin\"\n                title=\"Component Preview\"\n              />\n            </div>\n          )}\n        </TabsContent>\n        <TabsContent value=\"responsive\" className=\"p-4\">\n          <div className=\"space-y-4\">\n            {/* Mobile */}\n            <div>\n              <h4 className=\"mb-2 text-sm font-medium\">Mobile</h4>\n              <div className=\"rounded-lg border\">\n                <iframe\n                  src={previewUrl}\n                  className=\"h-[400px] w-[375px]\"\n                  sandbox=\"allow-scripts allow-modals allow-popups allow-same-origin\"\n                  title=\"Mobile Preview\"\n                />\n              </div>\n            </div>\n            {/* Tablet */}\n            <div>\n              <h4 className=\"mb-2 text-sm font-medium\">Tablet</h4>\n              <div className=\"rounded-lg border\">\n                <iframe\n                  src={previewUrl}\n                  className=\"h-[400px] w-[768px]\"\n                  sandbox=\"allow-scripts allow-modals allow-popups allow-same-origin\"\n                  title=\"Tablet Preview\"\n                />\n              </div>\n            </div>\n            {/* Desktop */}\n            <div>\n              <h4 className=\"mb-2 text-sm font-medium\">Desktop</h4>\n              <div className=\"rounded-lg border\">\n                <iframe\n                  src={previewUrl}\n                  className=\"h-[400px] w-full\"\n                  sandbox=\"allow-scripts allow-modals allow-popups allow-same-origin\"\n                  title=\"Desktop Preview\"\n                />\n              </div>\n            </div>\n          </div>\n        </TabsContent>\n      </Tabs>\n    </Card>\n  );\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/preview.tsx"
    },
    {
      "path": "_components/terminal.tsx",
      "content": "\"use client\";\n\nimport { ScrollArea } from \"@/registry/default/components/ui/scroll-area\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport Convert from \"ansi-to-html\";\nimport { useEffect, useMemo, useRef } from \"react\";\n\ninterface TerminalProps {\n  output: string[];\n  className?: string;\n}\n\n// Clean up control sequences that ansi-to-html doesn't handle\nfunction cleanAnsi(text: string): string {\n  return (\n    text\n      // Remove cursor hide/show\n      .replace(/\\u001b\\[\\?25[hl]/g, \"\")\n      // Remove clear line and move cursor sequences\n      .replace(/\\u001b\\[2K/g, \"\")\n      .replace(/\\u001b\\[1G/g, \"\")\n      // Remove other control sequences as needed\n      .replace(/\\u001b\\[\\d*[ABCDEFGJKST]/g, \"\")\n  );\n}\n\nexport function Terminal({ output, className }: TerminalProps) {\n  const scrollRef = useRef<HTMLDivElement>(null);\n  const scrollAreaRef = useRef<HTMLDivElement>(null);\n  const convert = useMemo(\n    () =>\n      new Convert({\n        fg: \"#D4D4D4\",\n        bg: \"#1E1E1E\",\n        newline: false,\n        escapeXML: true,\n        stream: true,\n      }),\n    []\n  );\n\n  // Auto-scroll to bottom when output changes\n  useEffect(() => {\n    if (scrollRef.current && scrollAreaRef.current) {\n      const scrollElement = scrollRef.current;\n      const viewportElement = scrollAreaRef.current;\n\n      // Check if scroll is near bottom before auto-scrolling\n      const isNearBottom =\n        viewportElement.scrollHeight -\n          viewportElement.scrollTop -\n          viewportElement.clientHeight <\n        50;\n\n      if (isNearBottom) {\n        scrollElement.scrollIntoView({ behavior: \"smooth\", block: \"end\" });\n      }\n    }\n  }, [output]);\n\n  // Process the output to handle newlines properly\n  const processedOutput = useMemo(() => {\n    return output.join(\"\\n\").split(/\\r?\\n/);\n  }, [output]);\n\n  return (\n    <ScrollArea className={cn(\"bg-[#1E1E1E]\", className)} ref={scrollAreaRef}>\n      <div className=\"p-4 font-mono text-sm text-[#D4D4D4] leading-5\">\n        {processedOutput.map((line, i) => (\n          <div\n            key={i}\n            className=\"min-h-[6px]\"\n            dangerouslySetInnerHTML={{\n              __html: convert.toHtml(cleanAnsi(line)) || \"&nbsp;\",\n            }}\n          />\n        ))}\n        <div ref={scrollRef} />\n      </div>\n    </ScrollArea>\n  );\n}\n",
      "type": "registry:component",
      "target": "app/cli/_components/terminal.tsx"
    },
    {
      "path": "lib/registry-service.ts",
      "content": "import { Registry, RegistryItem } from './types'\n\nconst BUILT_IN_REGISTRIES = [\n\t{\n\t\tname: 'shadcn/ui',\n\t\turl: 'https://ui.shadcn.com/r',\n\t\tdescription: 'Official shadcn/ui component registry with customizable components and blocks',\n\t\tbaseComponentUrl: 'https://ui.shadcn.com/r',\n\t\tbaseBlockUrl: 'https://ui.shadcn.com/r'\n\t},\n\t{\n\t\tname: 'Magic UI',\n\t\turl: 'https://magicui.design/r',\n\t\tdescription: 'Beautiful animated components and effects for modern web applications',\n\t\tbaseComponentUrl: 'https://magicui.design/r',\n\t\tbaseBlockUrl: 'https://magicui.design/r'\n\t},\n\t{\n\t\tname: 'Bones Registry',\n\t\turl: 'https://registry.bones.sh',\n\t\tdescription: 'Community-driven component registry',\n\t\tbaseComponentUrl: 'https://registry.bones.sh',\n\t\tbaseBlockUrl: 'https://registry.bones.sh'\n\t}\n] as const\n\nconst STORAGE_KEY = 'reg-browser:custom-registries'\n\nexport type RegistryName = typeof BUILT_IN_REGISTRIES[number]['name']\n\nfunction getCustomRegistries(): Registry[] {\n\tif (typeof window === 'undefined') return []\n\tconst stored = localStorage.getItem(STORAGE_KEY)\n\treturn stored ? JSON.parse(stored) : []\n}\n\nfunction saveCustomRegistries(registries: Registry[]) {\n\tif (typeof window === 'undefined') return\n\tlocalStorage.setItem(STORAGE_KEY, JSON.stringify(registries))\n}\n\nexport async function getRegistries(): Promise<Registry[]> {\n\treturn [...BUILT_IN_REGISTRIES, ...getCustomRegistries()]\n}\n\nexport function addCustomRegistry(registry: Registry) {\n\tconst customRegistries = getCustomRegistries()\n\tcustomRegistries.push(registry)\n\tsaveCustomRegistries(customRegistries)\n}\n\nexport function removeCustomRegistry(name: string) {\n\tconst customRegistries = getCustomRegistries()\n\tconst filtered = customRegistries.filter(r => r.name !== name)\n\tsaveCustomRegistries(filtered)\n}\n\nexport async function getRegistry(name: RegistryName): Promise<Registry> {\n\tconst registry = [...BUILT_IN_REGISTRIES, ...getCustomRegistries()].find(r => r.name === name)\n\tif (!registry) {\n\t\tthrow new Error(`Registry ${name} not found`)\n\t}\n\treturn registry\n}\n\nexport async function fetchRegistryIndex(registryUrl: string): Promise<RegistryItem[]> {\n\t// Ensure URL ends with index.json for registry indexes\n\tconst url = registryUrl.endsWith('index.json') \n\t\t? registryUrl \n\t\t: registryUrl.endsWith('/') \n\t\t\t? `${registryUrl}index.json`\n\t\t\t: `${registryUrl}/index.json`;\n\n\tconst response = await fetch(url)\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to fetch registry: ${response.statusText}`)\n\t}\n\treturn response.json()\n}\n\nexport async function fetchItemDetails(\n\tbaseUrl: string,\n\titemName: string,\n\tstyle: string = 'default'\n): Promise<RegistryItem> {\n\t// Handle different registry URL structures\n\tconst baseUrlWithoutIndex = baseUrl.endsWith('/index.json')\n\t\t? baseUrl.replace('/index.json', '')\n\t\t: baseUrl\n\n\tconst detailsUrl = `${baseUrlWithoutIndex}/styles/${style}/${itemName}.json`\n\tconst componentUrl = detailsUrl // Store the full URL for installation\n\n\tconst response = await fetch(detailsUrl)\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to fetch item details: ${response.statusText}`)\n\t}\n\n\tconst data = await response.json()\n\n\t// Add the full component URL to the item data\n\treturn {\n\t\t...data,\n\t\tcomponentUrl\n\t}\n}\n\nexport function categorizeItems(items: RegistryItem[]): Record<string, RegistryItem[]> {\n\treturn items.reduce((acc, item) => {\n\t\tconst category = item.type === 'registry:block' ? 'Blocks' : 'Components'\n\t\tif (!acc[category]) {\n\t\t\tacc[category] = []\n\t\t}\n\t\tacc[category].push(item)\n\t\treturn acc\n\t}, {} as Record<string, RegistryItem[]>)\n}\n\nexport function groupItemsByType(items: RegistryItem[]): Record<string, RegistryItem[]> {\n\treturn items.reduce((acc, item) => {\n\t\tconst types = item.categories || ['Uncategorized']\n\t\ttypes.forEach(type => {\n\t\t\tif (!acc[type]) {\n\t\t\t\tacc[type] = []\n\t\t\t}\n\t\t\tacc[type].push(item)\n\t\t})\n\t\treturn acc\n\t}, {} as Record<string, RegistryItem[]>)\n}\n\nexport function searchItems(\n\titems: RegistryItem[],\n\tquery: string,\n\tfilters: {\n\t\ttype?: 'all' | 'components' | 'blocks'\n\t\tcategory?: string\n\t} = {}\n): RegistryItem[] {\n\treturn items.filter(item => {\n\t\t// Type filter\n\t\tif (filters.type && filters.type !== 'all') {\n\t\t\tif (filters.type === 'components' && item.type !== 'registry:ui') return false\n\t\t\tif (filters.type === 'blocks' && item.type !== 'registry:block') return false\n\t\t}\n\n\t\t// Category filter\n\t\tif (filters.category && filters.category !== 'all') {\n\t\t\tif (!item.categories?.includes(filters.category)) return false\n\t\t}\n\n\t\t// Search query\n\t\tif (query) {\n\t\t\tconst searchString = [\n\t\t\t\titem.name,\n\t\t\t\titem.description,\n\t\t\t\t...(item.categories || []),\n\t\t\t\t...(item.dependencies || [])\n\t\t\t].join(' ').toLowerCase()\n\n\t\t\treturn query.toLowerCase().split(' ').every(term => searchString.includes(term))\n\t\t}\n\n\t\treturn true\n\t})\n}\n",
      "type": "registry:lib",
      "target": "app/cli/lib/registry-service.ts"
    },
    {
      "path": "actions.ts",
      "content": "'use server'\n\nimport { spawn, exec } from 'child_process'\nimport { readdirSync, readFileSync } from 'fs'\nimport path from 'path'\nimport { promisify } from 'util'\n\nconst execAsync = promisify(exec)\n\nexport async function getProjectRoot(): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execAsync('git rev-parse --show-toplevel')\n\t\treturn stdout.trim()\n\t} catch (error) {\n\t\tthrow new Error('Not in a git repository')\n\t}\n}\n\nexport async function installComponent(componentUrl: string, projectRoot: string, overwrite: boolean = false): Promise<ReadableStream> {\n\tconst encoder = new TextEncoder()\n\n\treturn new ReadableStream({\n\t\tasync start(controller) {\n\t\t\ttry {\n\t\t\t\tconst args = ['shadcn@latest', 'add', componentUrl]\n\t\t\t\tif (overwrite) {\n\t\t\t\t\targs.push('--overwrite')\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tconst process = spawn('npx', args, { \n\t\t\t\t\tcwd: projectRoot,\n\t\t\t\t\tstdio: ['pipe', 'pipe', 'pipe']\n\t\t\t\t})\n\n\t\t\t\t// If not overwriting, automatically answer \"n\" to prompts\n\t\t\t\tif (!overwrite && process.stdin) {\n\t\t\t\t\tprocess.stdin.write('n\\n')\n\t\t\t\t\tprocess.stdin.end()\n\t\t\t\t}\n\n\t\t\t\tprocess.stdout?.on('data', (data) => {\n\t\t\t\t\tcontroller.enqueue(encoder.encode(data))\n\t\t\t\t})\n\n\t\t\t\tprocess.stderr?.on('data', (data) => {\n\t\t\t\t\tcontroller.enqueue(encoder.encode(data))\n\t\t\t\t})\n\n\t\t\t\tprocess.on('close', (code) => {\n\t\t\t\t\tif (code !== 0) {\n\t\t\t\t\t\tcontroller.enqueue(encoder.encode(`\\nProcess exited with code ${code}`))\n\t\t\t\t\t}\n\t\t\t\t\tcontroller.close()\n\t\t\t\t})\n\n\t\t\t\tprocess.on('error', (err) => {\n\t\t\t\t\tcontroller.enqueue(encoder.encode(`\\nError: ${err.message}`))\n\t\t\t\t\tcontroller.close()\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error occurred'\n\t\t\t\tcontroller.enqueue(encoder.encode(`\\nError: ${message}`))\n\t\t\t\tcontroller.close()\n\t\t\t}\n\t\t}\n\t})\n}\n\nexport async function getDependencies(projectRoot: string): Promise<{\n\tdependencies: Record<string, string>\n\tdevDependencies: Record<string, string>\n}> {\n\ttry {\n\t\tconst packageJsonPath = path.join(projectRoot, 'package.json')\n\t\tconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {\n\t\t\tdependencies: Record<string, string>\n\t\t\tdevDependencies: Record<string, string>\n\t\t}\n\n\t\treturn {\n\t\t\tdependencies: packageJson.dependencies || {},\n\t\t\tdevDependencies: packageJson.devDependencies || {}\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error('Failed to read package.json')\n\t}\n}\n\nexport async function getInstalledComponents(projectRoot: string): Promise<string[]> {\n\ttry {\n\t\tconst componentsDir = path.join(projectRoot, 'src/components/ui')\n\t\treturn readdirSync(componentsDir)\n\t\t\t.filter(file => file.endsWith('.tsx'))\n\t\t\t.map(file => file.replace('.tsx', ''))\n\t} catch (error) {\n\t\treturn []\n\t}\n}\n",
      "type": "registry:lib",
      "target": "app/cli/actions.ts"
    }
  ]
}