{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "blocks",
  "homepage": "https://blocks.so",
  "items": [
    {
      "name": "ai-01",
      "type": "registry:block",
      "title": "AI Chat with Voice Input",
      "description": "A ai chat with voice input block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dropdown-menu",
        "textarea"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/ai/ai-01.tsx",
          "type": "registry:component",
          "target": "components/ai-01.tsx",
          "content": "'use client';\n\nimport {\n  IconMicrophone,\n  IconPaperclip,\n  IconPlus,\n  IconSearch,\n  IconSend,\n  IconSparkles,\n  IconWaveSine,\n} from '@tabler/icons-react';\nimport type React from 'react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Textarea } from '@/components/ui/textarea';\nimport { cn } from '@/lib/utils';\n\nexport default function Ai01() {\n  const [message, setMessage] = useState('');\n  const [isExpanded, setIsExpanded] = useState(false);\n  const textareaRef = useRef<HTMLTextAreaElement>(null);\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  const send = () => {\n    if (!message.trim()) {\n      return;\n    }\n\n    setMessage('');\n    setIsExpanded(false);\n\n    if (textareaRef.current) {\n      textareaRef.current.style.height = 'auto';\n    }\n  };\n\n  const handleSubmit = (e: React.FormEvent) => {\n    e.preventDefault();\n    send();\n  };\n\n  const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n    setMessage(e.target.value);\n\n    if (textareaRef.current) {\n      textareaRef.current.style.height = 'auto';\n      textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;\n    }\n\n    setIsExpanded(e.target.value.length > 100 || e.target.value.includes('\\n'));\n  };\n\n  const handleKeyDown = (e: React.KeyboardEvent) => {\n    if (e.key === 'Enter' && !e.shiftKey) {\n      e.preventDefault();\n      send();\n    }\n  };\n\n  return (\n    <div className=\"w-full\">\n      <h1 className=\"mx-auto mb-6 max-w-2xl text-balance text-center font-semibold text-3xl text-foreground leading-tight tracking-tight\">\n        How can I help you today?\n      </h1>\n\n      <form className=\"group/composer w-full\" onSubmit={handleSubmit}>\n        <input className=\"sr-only\" multiple ref={fileInputRef} type=\"file\" />\n\n        <div\n          className={cn(\n            'mx-auto w-full max-w-2xl cursor-text overflow-clip bg-background bg-clip-padding p-2.5 shadow-[0_0_0_1px_oklch(0_0_0/0.08),0_1px_2px_-1px_oklch(0_0_0/0.08),0_4px_12px_-2px_oklch(0_0_0/0.08)] dark:bg-muted/50 dark:shadow-[0_0_0_1px_oklch(1_0_0/0.1)]',\n            isExpanded\n              ? \"grid rounded-[28px] [grid-template-areas:'header'_'primary'_'footer'] [grid-template-columns:1fr] [grid-template-rows:auto_1fr_auto]\"\n              : \"grid rounded-full [grid-template-areas:'header_header_header'_'leading_primary_trailing'_'._footer_.'] [grid-template-columns:auto_1fr_auto] [grid-template-rows:auto_1fr_auto]\"\n          )}\n        >\n          <div\n            className={cn(\n              'flex overflow-x-hidden',\n              isExpanded\n                ? 'px-2 pt-1.5 pb-2'\n                : '-my-2.5 min-h-14 items-center px-1.5'\n            )}\n            style={{ gridArea: 'primary' }}\n          >\n            <div className=\"max-h-52 flex-1 overflow-auto\">\n              <Textarea\n                className=\"scrollbar-thin min-h-0 resize-none rounded-none border-0 p-0 text-base placeholder:text-muted-foreground focus-visible:ring-0 focus-visible:ring-offset-0 md:text-base dark:bg-transparent\"\n                onChange={handleTextareaChange}\n                onKeyDown={handleKeyDown}\n                placeholder=\"Ask anything\"\n                ref={textareaRef}\n                rows={1}\n                value={message}\n              />\n            </div>\n          </div>\n\n          <div\n            className={cn('flex items-center', { hidden: isExpanded })}\n            style={{ gridArea: 'leading' }}\n          >\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <Button\n                    aria-label=\"Add attachments\"\n                    className=\"rounded-full transition-[scale,background-color] duration-150 ease-out active:scale-[0.96]\"\n                    size=\"icon-lg\"\n                    type=\"button\"\n                    variant=\"ghost\"\n                  />\n                }\n              >\n                <IconPlus\n                  className=\"size-5 text-muted-foreground\"\n                  stroke={1.5}\n                />\n              </DropdownMenuTrigger>\n\n              <DropdownMenuContent\n                align=\"start\"\n                className=\"w-64 rounded-[16px] p-1.5\"\n              >\n                <DropdownMenuGroup className=\"space-y-1\">\n                  <DropdownMenuItem\n                    className=\"rounded-[10px] px-2.5 py-2\"\n                    onClick={() => fileInputRef.current?.click()}\n                  >\n                    <IconPaperclip\n                      className=\"opacity-60\"\n                      size={20}\n                      stroke={1.5}\n                    />\n                    Add photos & files\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    className=\"rounded-[10px] px-2.5 py-2\"\n                    onClick={() => {}}\n                  >\n                    <IconSparkles\n                      className=\"opacity-60\"\n                      size={20}\n                      stroke={1.5}\n                    />\n                    Agent mode\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    className=\"rounded-[10px] px-2.5 py-2\"\n                    onClick={() => {}}\n                  >\n                    <IconSearch className=\"opacity-60\" size={20} stroke={1.5} />\n                    Deep Research\n                  </DropdownMenuItem>\n                </DropdownMenuGroup>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </div>\n\n          <div\n            className=\"flex items-center gap-2\"\n            style={{ gridArea: isExpanded ? 'footer' : 'trailing' }}\n          >\n            <div className=\"ms-auto flex items-center gap-1.5\">\n              <Button\n                aria-label=\"Record audio message\"\n                className=\"rounded-full transition-[scale,background-color] duration-150 ease-out active:scale-[0.96]\"\n                size=\"icon-lg\"\n                type=\"button\"\n                variant=\"ghost\"\n              >\n                <IconMicrophone\n                  className=\"size-5 text-muted-foreground\"\n                  stroke={1.5}\n                />\n              </Button>\n\n              <Button\n                aria-label=\"Audio visualization\"\n                className=\"rounded-full transition-[scale,background-color] duration-150 ease-out active:scale-[0.96]\"\n                size=\"icon-lg\"\n                type=\"button\"\n                variant=\"ghost\"\n              >\n                <IconWaveSine\n                  className=\"size-5 text-muted-foreground\"\n                  stroke={1.5}\n                />\n              </Button>\n\n              <Button\n                aria-label=\"Send message\"\n                className=\"rounded-full transition-[scale,opacity,background-color] duration-150 ease-out active:scale-[0.96] disabled:opacity-40\"\n                disabled={!message.trim()}\n                size=\"icon-lg\"\n                type=\"submit\"\n              >\n                <IconSend className=\"size-4.5\" stroke={1.5} />\n              </Button>\n            </div>\n          </div>\n        </div>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "ai"
      ]
    },
    {
      "name": "ai-02",
      "type": "registry:block",
      "title": "AI Chat with Model Selection",
      "description": "A ai chat with model selection block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "select",
        "textarea"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/ai/ai-02.tsx",
          "type": "registry:component",
          "target": "components/ai-02.tsx",
          "content": "'use client';\n\nimport {\n  IconAlertTriangle,\n  IconArrowUp,\n  IconCloud,\n  IconFileSpark,\n  IconGauge,\n  IconPhotoScan,\n} from '@tabler/icons-react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Textarea } from '@/components/ui/textarea';\n\nconst PROMPTS = [\n  {\n    icon: IconFileSpark,\n    text: 'Write documentation',\n    prompt:\n      'Write comprehensive documentation for this codebase, including setup instructions, API references, and usage examples.',\n  },\n  {\n    icon: IconGauge,\n    text: 'Optimize performance',\n    prompt:\n      'Analyze the codebase for performance bottlenecks and suggest optimizations to improve loading times and runtime efficiency.',\n  },\n  {\n    icon: IconAlertTriangle,\n    text: 'Find and fix 3 bugs',\n    prompt:\n      'Scan through the codebase to identify and fix 3 critical bugs, providing detailed explanations for each fix.',\n  },\n];\n\nconst MODELS = [\n  {\n    value: 'gpt-5',\n    name: 'GPT-5',\n    description: 'Most advanced model',\n    max: true,\n  },\n  {\n    value: 'gpt-4o',\n    name: 'GPT-4o',\n    description: 'Fast and capable',\n  },\n  {\n    value: 'claude-3.5',\n    name: 'Claude 3.5 Sonnet',\n    description: 'Great for coding tasks',\n  },\n];\n\nexport default function Ai02() {\n  const [inputValue, setInputValue] = useState('');\n  const [modelValue, setModelValue] = useState(MODELS[0].value);\n  const selectedModel = MODELS.find((m) => m.value === modelValue) ?? MODELS[0];\n  const inputRef = useRef<HTMLTextAreaElement>(null);\n\n  const handlePromptClick = (prompt: string) => {\n    if (inputRef.current) {\n      inputRef.current.value = prompt;\n      setInputValue(prompt);\n      inputRef.current.focus();\n    }\n  };\n\n  const maxBadge = (\n    <span className=\"inline-flex h-[15px] items-center rounded-[4px] border border-border/80 bg-muted/40 px-[5px]\">\n      <span\n        className=\"font-bold text-[9px] uppercase leading-none tracking-[0.06em]\"\n        style={{\n          background:\n            'linear-gradient(to right, rgb(129, 161, 193), rgb(125, 124, 155))',\n          WebkitBackgroundClip: 'text',\n          WebkitTextFillColor: 'transparent',\n        }}\n      >\n        MAX\n      </span>\n    </span>\n  );\n\n  return (\n    <div className=\"flex w-[calc(42rem-5rem)] flex-col gap-4\">\n      <div className=\"flex min-h-[120px] cursor-text flex-col rounded-2xl border border-border bg-card shadow-lg\">\n        <div className=\"relative max-h-[258px] flex-1 overflow-y-auto\">\n          <Textarea\n            className=\"min-h-[48.4px] w-full resize-none whitespace-pre-wrap break-words border-0 bg-transparent! p-3 text-[16px] text-foreground shadow-none outline-none transition-[padding] duration-200 ease-in-out focus-visible:ring-0 focus-visible:ring-offset-0\"\n            onChange={(e) => setInputValue(e.target.value)}\n            placeholder=\"Ask anything\"\n            ref={inputRef}\n            value={inputValue}\n          />\n        </div>\n\n        <div className=\"flex min-h-[40px] items-center gap-2 p-2 pb-1\">\n          <div className=\"flex size-7 items-center justify-center rounded-full bg-muted\">\n            <IconCloud className=\"size-4 text-muted-foreground\" />\n          </div>\n\n          <Select\n            onValueChange={(value) => value && setModelValue(value)}\n            value={modelValue}\n          >\n            <SelectTrigger\n              className=\"h-7 rounded-full border-transparent bg-transparent px-2 font-medium text-foreground text-sm shadow-none transition-[background-color,color] hover:bg-muted aria-expanded:bg-muted dark:bg-transparent dark:hover:bg-muted\"\n              size=\"sm\"\n            >\n              <SelectValue>\n                <span className=\"flex items-center gap-1.5\">\n                  <span>{selectedModel.name}</span>\n                  {selectedModel.max && maxBadge}\n                </span>\n              </SelectValue>\n            </SelectTrigger>\n            <SelectContent\n              align=\"start\"\n              alignItemWithTrigger={false}\n              className=\"w-64 p-1\"\n              sideOffset={6}\n            >\n              {MODELS.map((model) => (\n                <SelectItem\n                  className=\"rounded-md py-1.5\"\n                  key={model.value}\n                  value={model.value}\n                >\n                  <span className=\"flex flex-col gap-0.5\">\n                    <span className=\"flex items-center gap-1.5 font-medium\">\n                      {model.name}\n                      {model.max && maxBadge}\n                    </span>\n                    <span className=\"text-muted-foreground text-xs\">\n                      {model.description}\n                    </span>\n                  </span>\n                </SelectItem>\n              ))}\n            </SelectContent>\n          </Select>\n\n          <div className=\"ml-auto flex items-center gap-1.5\">\n            <Button\n              aria-label=\"Attach images\"\n              className=\"rounded-full text-muted-foreground transition-[background-color,color] hover:text-foreground\"\n              size=\"icon-sm\"\n              title=\"Attach images\"\n              variant=\"ghost\"\n            >\n              <IconPhotoScan className=\"size-4.5\" />\n            </Button>\n\n            <Button\n              aria-label=\"Send message\"\n              className=\"rounded-full transition-[background-color,scale,opacity] active:not-disabled:scale-[0.96]\"\n              disabled={!inputValue}\n              size=\"icon-sm\"\n            >\n              <IconArrowUp className=\"size-4\" />\n            </Button>\n          </div>\n        </div>\n      </div>\n\n      <div className=\"flex flex-wrap justify-center gap-2\">\n        {PROMPTS.map((button) => {\n          const IconComponent = button.icon;\n          return (\n            <Button\n              className=\"h-8 gap-1.5 rounded-full border-border bg-background pr-3.5 pl-2.5 font-normal text-foreground text-sm shadow-[0_1px_2px_oklch(0_0_0/0.05)] transition-[background-color,color,scale,box-shadow] duration-150 ease-out hover:bg-muted active:scale-[0.96] dark:bg-muted/40 dark:hover:bg-muted\"\n              key={button.text}\n              onClick={() => handlePromptClick(button.prompt)}\n              variant=\"outline\"\n            >\n              <IconComponent\n                className=\"size-4 text-muted-foreground transition-colors duration-150 group-hover/button:text-foreground\"\n                strokeWidth={1.75}\n              />\n              {button.text}\n            </Button>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "ai"
      ]
    },
    {
      "name": "ai-03",
      "type": "registry:block",
      "title": "AI Chat Compact Interface",
      "description": "A ai chat compact interface block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dropdown-menu",
        "textarea"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/ai/ai-03.tsx",
          "type": "registry:component",
          "target": "components/ai-03.tsx",
          "content": "'use client';\n\nimport {\n  IconBolt,\n  IconChevronDown,\n  IconCircle,\n  IconCircleDashed,\n  IconCloud,\n  IconCode,\n  IconDeviceLaptop,\n  IconHistory,\n  IconPaperclip,\n  IconPlus,\n  IconProgress,\n  IconRobot,\n  IconSend,\n  IconUser,\n  IconWand,\n  IconWorld,\n} from '@tabler/icons-react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Textarea } from '@/components/ui/textarea';\nimport { cn } from '@/lib/utils';\n\nexport default function Ai03() {\n  const [input, setInput] = useState('');\n  const [selectedModel, setSelectedModel] = useState('Local');\n  const [selectedAgent, setSelectedAgent] = useState('Agent');\n  const [selectedPerformance, setSelectedPerformance] = useState('High');\n  const [autoMode, setAutoMode] = useState(false);\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  const handleSubmit = (e: React.FormEvent) => {\n    e.preventDefault();\n    if (input.trim()) {\n    }\n  };\n\n  return (\n    <div className=\"w-xl\">\n      <div className=\"overflow-hidden rounded-2xl border border-border bg-background\">\n        <input\n          className=\"sr-only\"\n          multiple\n          onChange={(e) => {}}\n          ref={fileInputRef}\n          type=\"file\"\n        />\n\n        <div className=\"grow px-3 pt-3 pb-2\">\n          <form onSubmit={handleSubmit}>\n            <Textarea\n              className=\"max-h-[25vh] min-h-10 w-full resize-none border-0 border-none bg-transparent! p-0 text-foreground text-sm placeholder-muted-foreground shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0\"\n              onChange={(e) => setInput(e.target.value)}\n              onInput={(e) => {\n                const target = e.target as HTMLTextAreaElement;\n                target.style.height = 'auto';\n                target.style.height = target.scrollHeight + 'px';\n              }}\n              placeholder=\"Ask anything\"\n              rows={1}\n              value={input}\n            />\n          </form>\n        </div>\n\n        <div className=\"mb-2 flex items-center justify-between px-2\">\n          <div className=\"flex items-center gap-1\">\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <Button\n                    className=\"h-7 w-7 rounded-full border border-border p-0 hover:bg-accent\"\n                    size=\"sm\"\n                    variant=\"ghost\"\n                  />\n                }\n              >\n                <IconPlus className=\"size-3\" />\n              </DropdownMenuTrigger>\n\n              <DropdownMenuContent\n                align=\"start\"\n                className=\"max-w-xs rounded-2xl p-1.5\"\n              >\n                <DropdownMenuGroup className=\"space-y-1\">\n                  <DropdownMenuItem\n                    className=\"rounded-[calc(1rem-6px)] text-xs\"\n                    onClick={() => fileInputRef.current?.click()}\n                  >\n                    <IconPaperclip className=\"opacity-60\" size={16} />\n                    Attach Files\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    className=\"rounded-[calc(1rem-6px)] text-xs\"\n                    onClick={() => {}}\n                  >\n                    <IconCode className=\"opacity-60\" size={16} />\n                    Code Interpreter\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    className=\"rounded-[calc(1rem-6px)] text-xs\"\n                    onClick={() => {}}\n                  >\n                    <IconWorld className=\"opacity-60\" size={16} />\n                    Web Search\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    className=\"rounded-[calc(1rem-6px)] text-xs\"\n                    onClick={() => {}}\n                  >\n                    <IconHistory className=\"opacity-60\" size={16} />\n                    Chat History\n                  </DropdownMenuItem>\n                </DropdownMenuGroup>\n              </DropdownMenuContent>\n            </DropdownMenu>\n\n            <Button\n              className={cn(\n                'h-7 rounded-full border border-border px-2 hover:bg-accent ',\n                {\n                  'border-primary/30 bg-primary/10 text-primary': autoMode,\n                  'text-muted-foreground': !autoMode,\n                }\n              )}\n              onClick={() => setAutoMode(!autoMode)}\n              size=\"sm\"\n              variant=\"ghost\"\n            >\n              <IconWand className=\"size-3\" />\n              <span className=\"text-xs\">Auto</span>\n            </Button>\n          </div>\n\n          <div>\n            <Button\n              className=\"size-7 rounded-full bg-primary p-0 disabled:cursor-not-allowed disabled:opacity-50\"\n              disabled={!input.trim()}\n              onClick={handleSubmit}\n              type=\"submit\"\n            >\n              <IconSend className=\"size-3 fill-primary\" />\n            </Button>\n          </div>\n        </div>\n      </div>\n\n      <div className=\"flex items-center gap-0 pt-2\">\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <Button\n                className=\"h-6 rounded-full border border-transparent px-2 text-muted-foreground text-xs hover:bg-accent\"\n                size=\"sm\"\n                variant=\"ghost\"\n              />\n            }\n          >\n            <IconDeviceLaptop className=\"size-3\" />\n            <span>{selectedModel}</span>\n            <IconChevronDown className=\"size-3\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"start\"\n            className=\"max-w-xs rounded-2xl border-border bg-popover p-1.5\"\n          >\n            <DropdownMenuGroup className=\"space-y-1\">\n              <DropdownMenuItem\n                className=\"rounded-[calc(1rem-6px)] text-xs\"\n                onClick={() => setSelectedModel('Local')}\n              >\n                <IconDeviceLaptop className=\"opacity-60\" size={16} />\n                Local\n              </DropdownMenuItem>\n              <DropdownMenuItem\n                className=\"rounded-[calc(1rem-6px)] text-xs\"\n                onClick={() => setSelectedModel('Cloud')}\n              >\n                <IconCloud className=\"opacity-60\" size={16} />\n                Cloud\n              </DropdownMenuItem>\n            </DropdownMenuGroup>\n          </DropdownMenuContent>\n        </DropdownMenu>\n\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <Button\n                className=\"h-6 rounded-full border border-transparent px-2 text-muted-foreground text-xs hover:bg-accent\"\n                size=\"sm\"\n                variant=\"ghost\"\n              />\n            }\n          >\n            <IconUser className=\"size-3\" />\n            <span>{selectedAgent}</span>\n            <IconChevronDown className=\"size-3\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"start\"\n            className=\"max-w-xs rounded-2xl border-border bg-popover p-1.5\"\n          >\n            <DropdownMenuGroup className=\"space-y-1\">\n              <DropdownMenuItem\n                className=\"rounded-[calc(1rem-6px)] text-xs\"\n                onClick={() => setSelectedAgent('Agent')}\n              >\n                <IconUser className=\"opacity-60\" size={16} />\n                Agent\n              </DropdownMenuItem>\n              <DropdownMenuItem\n                className=\"rounded-[calc(1rem-6px)] text-xs\"\n                onClick={() => setSelectedAgent('Assistant')}\n              >\n                <IconRobot className=\"opacity-60\" size={16} />\n                Assistant\n              </DropdownMenuItem>\n            </DropdownMenuGroup>\n          </DropdownMenuContent>\n        </DropdownMenu>\n\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <Button\n                className=\"h-6 rounded-full border border-transparent px-2 text-muted-foreground text-xs hover:bg-accent\"\n                size=\"sm\"\n                variant=\"ghost\"\n              />\n            }\n          >\n            <IconBolt className=\"size-3\" />\n            <span>{selectedPerformance}</span>\n            <IconChevronDown className=\"size-3\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"start\"\n            className=\"max-w-xs rounded-2xl border-border bg-popover p-1.5\"\n          >\n            <DropdownMenuGroup className=\"space-y-1\">\n              <DropdownMenuItem\n                className=\"rounded-[calc(1rem-6px)] text-xs\"\n                onClick={() => setSelectedPerformance('High')}\n              >\n                <IconCircle className=\"opacity-60\" size={16} />\n                High\n              </DropdownMenuItem>\n              <DropdownMenuItem\n                className=\"rounded-[calc(1rem-6px)] text-xs\"\n                onClick={() => setSelectedPerformance('Medium')}\n              >\n                <IconProgress className=\"opacity-60\" size={16} />\n                Medium\n              </DropdownMenuItem>\n              <DropdownMenuItem\n                className=\"rounded-[calc(1rem-6px)] text-xs\"\n                onClick={() => setSelectedPerformance('Low')}\n              >\n                <IconCircleDashed className=\"opacity-60\" size={16} />\n                Low\n              </DropdownMenuItem>\n            </DropdownMenuGroup>\n          </DropdownMenuContent>\n        </DropdownMenu>\n\n        <div className=\"flex-1\" />\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "ai"
      ]
    },
    {
      "name": "ai-04",
      "type": "registry:block",
      "title": "AI Chat with File Attachments",
      "description": "A ai chat with file attachments block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "button",
        "dropdown-menu",
        "label",
        "switch",
        "textarea"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/ai/ai-04.tsx",
          "type": "registry:component",
          "target": "components/ai-04.tsx",
          "content": "'use client';\n\nimport {\n  IconAdjustmentsHorizontal,\n  IconArrowUp,\n  IconBrandFigma,\n  IconCamera,\n  IconCirclePlus,\n  IconClipboard,\n  IconFileUpload,\n  IconHistory,\n  IconLayoutDashboard,\n  IconLink,\n  IconPaperclip,\n  IconPlayerPlay,\n  IconPlus,\n  IconSparkles,\n  IconTemplate,\n  IconX,\n} from '@tabler/icons-react';\nimport Image from 'next/image';\nimport { useRef, useState } from 'react';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Label } from '@/components/ui/label';\nimport { Switch } from '@/components/ui/switch';\nimport { Textarea } from '@/components/ui/textarea';\nimport { cn } from '@/lib/utils';\n\ninterface AttachedFile {\n  id: string;\n  name: string;\n  file: File;\n  preview?: string;\n}\n\nconst ACTIONS = [\n  { id: 'clone-screenshot', icon: IconCamera, label: 'Clone a Screenshot' },\n  { id: 'import-figma', icon: IconBrandFigma, label: 'Import from Figma' },\n  { id: 'upload-project', icon: IconFileUpload, label: 'Upload a Project' },\n  { id: 'landing-page', icon: IconLayoutDashboard, label: 'Landing Page' },\n];\n\nexport default function Ai04({\n  onSubmit,\n}: {\n  onSubmit?: (prompt: string) => void;\n}) {\n  const [prompt, setPrompt] = useState('');\n  const [isDragOver, setIsDragOver] = useState(false);\n  const [attachedFiles, setAttachedFiles] = useState<AttachedFile[]>([]);\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  const [settings, setSettings] = useState({\n    autoComplete: true,\n    streaming: false,\n    showHistory: false,\n  });\n\n  const generateFileId = () => Math.random().toString(36).substring(7);\n  const processFiles = (files: File[]) => {\n    for (const file of files) {\n      const fileId = generateFileId();\n      const attachedFile: AttachedFile = {\n        id: fileId,\n        name: file.name,\n        file,\n      };\n\n      if (file.type.startsWith('image/')) {\n        const reader = new FileReader();\n        reader.onload = () => {\n          setAttachedFiles((prev) =>\n            prev.map((f) =>\n              f.id === fileId ? { ...f, preview: reader.result as string } : f\n            )\n          );\n        };\n        reader.readAsDataURL(file);\n      }\n\n      setAttachedFiles((prev) => [...prev, attachedFile]);\n    }\n  };\n  const submitPrompt = () => {\n    if (prompt.trim() && onSubmit) {\n      onSubmit(prompt.trim());\n      setPrompt('');\n    }\n  };\n  const updateSetting = (key: keyof typeof settings, value: boolean) => {\n    setSettings((prev) => ({ ...prev, [key]: value }));\n  };\n  const handleSubmit = (e: React.FormEvent) => {\n    e.preventDefault();\n    submitPrompt();\n  };\n  const handleDragOver = (e: React.DragEvent) => {\n    e.preventDefault();\n    setIsDragOver(true);\n  };\n  const handleDragLeave = (e: React.DragEvent) => {\n    e.preventDefault();\n    setIsDragOver(false);\n  };\n  const handleDrop = (e: React.DragEvent) => {\n    e.preventDefault();\n    setIsDragOver(false);\n\n    const files = Array.from(e.dataTransfer.files);\n    if (files.length > 0) {\n      processFiles(files);\n    }\n  };\n  const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n    setPrompt(e.target.value);\n  };\n  const handleKeyDown = (e: React.KeyboardEvent) => {\n    if (e.key === 'Enter' && !e.shiftKey) {\n      e.preventDefault();\n      submitPrompt();\n    }\n  };\n\n  const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const files = Array.from(e.target.files || []);\n    processFiles(files);\n\n    if (fileInputRef.current) {\n      fileInputRef.current.value = '';\n    }\n  };\n\n  const handleRemoveFile = (fileId: string) => {\n    setAttachedFiles((prev) => prev.filter((file) => file.id !== fileId));\n  };\n\n  return (\n    <div className=\"mx-auto flex w-full flex-col gap-4\">\n      <h1 className=\"text-balance text-pretty text-center font-heading font-semibold text-[29px] text-foreground tracking-tighter sm:text-[32px] md:text-[46px]\">\n        Prompt. Refine. Ship.\n      </h1>\n      <h2 className=\"-my-5 text-balance pb-4 text-center text-muted-foreground text-xl\">\n        Build real, working software just by describing it\n      </h2>\n\n      <div className=\"relative z-10 mx-auto flex w-full max-w-2xl flex-col content-center\">\n        <form\n          className=\"overflow-visible rounded-xl border p-2 transition-colors duration-200 focus-within:border-ring\"\n          onDragLeave={handleDragLeave}\n          onDragOver={handleDragOver}\n          onDrop={handleDrop}\n          onSubmit={handleSubmit}\n        >\n          {attachedFiles.length > 0 && (\n            <div className=\"relative mb-2 flex w-fit items-center gap-2 overflow-hidden\">\n              {attachedFiles.map((file) => (\n                <Badge\n                  className=\"group relative h-6 max-w-30 cursor-pointer overflow-hidden px-0 text-[13px] transition-colors hover:bg-accent\"\n                  key={file.id}\n                  variant=\"outline\"\n                >\n                  <span className=\"flex h-full items-center gap-1.5 overflow-hidden pl-1 font-normal\">\n                    <div className=\"relative flex h-4 min-w-4 items-center justify-center\">\n                      {file.preview ? (\n                        <Image\n                          alt={file.name}\n                          className=\"absolute inset-0 h-4 w-4 rounded border object-cover\"\n                          height={16}\n                          src={file.preview}\n                          width={16}\n                        />\n                      ) : (\n                        <IconPaperclip className=\"opacity-60\" size={12} />\n                      )}\n                    </div>\n                    <span className=\"inline overflow-hidden truncate pr-1.5\">\n                      {file.name}\n                    </span>\n                  </span>\n                  <button\n                    className=\"absolute right-1 z-10 rounded-sm p-0.5 text-muted-foreground opacity-0 focus-visible:bg-accent focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-background group-hover:opacity-100\"\n                    onClick={() => handleRemoveFile(file.id)}\n                    type=\"button\"\n                  >\n                    <IconX size={12} />\n                  </button>\n                </Badge>\n              ))}\n            </div>\n          )}\n          <Textarea\n            className=\"max-h-50 min-h-12 resize-none rounded-none border-none bg-transparent! p-0 text-sm shadow-none focus-visible:border-transparent focus-visible:ring-0\"\n            onChange={handleTextareaChange}\n            onKeyDown={handleKeyDown}\n            placeholder=\"Ask anything\"\n            value={prompt}\n          />\n\n          <div className=\"flex items-center gap-1\">\n            <div className=\"flex items-end gap-0.5 sm:gap-1\">\n              <input\n                className=\"sr-only\"\n                multiple\n                onChange={handleFileSelect}\n                ref={fileInputRef}\n                type=\"file\"\n              />\n\n              <DropdownMenu>\n                <DropdownMenuTrigger\n                  render={\n                    <Button\n                      aria-label=\"Add attachments\"\n                      className=\"ml-[-2px] rounded-md\"\n                      size=\"icon-sm\"\n                      type=\"button\"\n                      variant=\"ghost\"\n                    />\n                  }\n                >\n                  <IconPlus size={16} />\n                </DropdownMenuTrigger>\n                <DropdownMenuContent\n                  align=\"start\"\n                  className=\"max-w-xs rounded-2xl p-1.5\"\n                >\n                  <DropdownMenuGroup className=\"space-y-1\">\n                    <DropdownMenuItem\n                      className=\"rounded-md text-xs\"\n                      onClick={() => fileInputRef.current?.click()}\n                    >\n                      <div className=\"flex items-center gap-2\">\n                        <IconPaperclip\n                          className=\"text-muted-foreground\"\n                          size={16}\n                        />\n                        <span>Attach Files</span>\n                      </div>\n                    </DropdownMenuItem>\n                    <DropdownMenuItem className=\"rounded-md text-xs\">\n                      <div className=\"flex items-center gap-2\">\n                        <IconLink className=\"text-muted-foreground\" size={16} />\n                        <span>Import from URL</span>\n                      </div>\n                    </DropdownMenuItem>\n                    <DropdownMenuItem className=\"rounded-md text-xs\">\n                      <div className=\"flex items-center gap-2\">\n                        <IconClipboard\n                          className=\"text-muted-foreground\"\n                          size={16}\n                        />\n                        <span>Paste from Clipboard</span>\n                      </div>\n                    </DropdownMenuItem>\n                    <DropdownMenuItem className=\"rounded-md text-xs\">\n                      <div className=\"flex items-center gap-2\">\n                        <IconTemplate\n                          className=\"text-muted-foreground\"\n                          size={16}\n                        />\n                        <span>Use Template</span>\n                      </div>\n                    </DropdownMenuItem>\n                  </DropdownMenuGroup>\n                </DropdownMenuContent>\n              </DropdownMenu>\n\n              <DropdownMenu>\n                <DropdownMenuTrigger\n                  render={\n                    <Button\n                      aria-label=\"Adjust settings\"\n                      className=\"rounded-md\"\n                      size=\"icon-sm\"\n                      type=\"button\"\n                      variant=\"ghost\"\n                    />\n                  }\n                >\n                  <IconAdjustmentsHorizontal size={16} />\n                </DropdownMenuTrigger>\n                <DropdownMenuContent\n                  align=\"start\"\n                  className=\"w-48 rounded-2xl p-3\"\n                >\n                  <DropdownMenuGroup className=\"space-y-3\">\n                    <div className=\"flex items-center justify-between\">\n                      <div className=\"flex items-center gap-2\">\n                        <IconSparkles\n                          className=\"text-muted-foreground\"\n                          size={16}\n                        />\n                        <Label className=\"text-xs\">Auto-complete</Label>\n                      </div>\n                      <Switch\n                        checked={settings.autoComplete}\n                        className=\"scale-75\"\n                        onCheckedChange={(value) =>\n                          updateSetting('autoComplete', value)\n                        }\n                      />\n                    </div>\n\n                    <div className=\"flex items-center justify-between\">\n                      <div className=\"flex items-center gap-2\">\n                        <IconPlayerPlay\n                          className=\"text-muted-foreground\"\n                          size={16}\n                        />\n                        <Label className=\"text-xs\">Streaming</Label>\n                      </div>\n                      <Switch\n                        checked={settings.streaming}\n                        className=\"scale-75\"\n                        onCheckedChange={(value) =>\n                          updateSetting('streaming', value)\n                        }\n                      />\n                    </div>\n\n                    <div className=\"flex items-center justify-between\">\n                      <div className=\"flex items-center gap-2\">\n                        <IconHistory\n                          className=\"text-muted-foreground\"\n                          size={16}\n                        />\n                        <Label className=\"text-xs\">Show History</Label>\n                      </div>\n                      <Switch\n                        checked={settings.showHistory}\n                        className=\"scale-75\"\n                        onCheckedChange={(value) =>\n                          updateSetting('showHistory', value)\n                        }\n                      />\n                    </div>\n                  </DropdownMenuGroup>\n                </DropdownMenuContent>\n              </DropdownMenu>\n            </div>\n\n            <div className=\"ml-auto flex items-center gap-0.5 sm:gap-1\">\n              <Button\n                aria-label=\"Send message\"\n                className=\"rounded-md\"\n                disabled={!prompt.trim()}\n                size=\"icon-sm\"\n                type=\"submit\"\n                variant=\"default\"\n              >\n                <IconArrowUp size={16} />\n              </Button>\n            </div>\n          </div>\n\n          <div\n            className={cn(\n              'pointer-events-none absolute inset-0 z-20 flex items-center justify-center rounded-[inherit] border border-border border-dashed bg-muted text-foreground text-sm transition-opacity duration-200',\n              isDragOver ? 'opacity-100' : 'opacity-0'\n            )}\n          >\n            <span className=\"flex w-full items-center justify-center gap-1 font-medium\">\n              <IconCirclePlus className=\"min-w-4\" size={16} />\n              Drop files here to add as attachments\n            </span>\n          </div>\n        </form>\n      </div>\n\n      <div className=\"mx-auto flex min-h-0 max-w-250 shrink-0 flex-wrap items-center justify-center gap-3\">\n        {ACTIONS.map((action) => (\n          <Button\n            className=\"gap-2 rounded-full\"\n            key={action.id}\n            size=\"sm\"\n            variant=\"outline\"\n          >\n            <action.icon size={16} />\n            {action.label}\n          </Button>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "ai"
      ]
    },
    {
      "name": "ai-05",
      "type": "registry:block",
      "title": "AI Elements Chat",
      "description": "A ai elements chat block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button"
      ],
      "dependencies": [
        "@tabler/icons-react",
        "ai"
      ],
      "files": [
        {
          "path": "content/components/ai/ai-05.tsx",
          "type": "registry:component",
          "target": "components/ai-05.tsx",
          "content": "'use client';\n\n// Install AI Elements components:\n// npx ai-elements@latest add conversation message prompt-input\n\nimport {\n  IconAdjustmentsHorizontal,\n  IconBolt,\n  IconPaperclip,\n  IconPlus,\n} from '@tabler/icons-react';\nimport type { ChatStatus } from 'ai';\nimport { useState } from 'react';\nimport {\n  Conversation,\n  ConversationContent,\n  ConversationScrollButton,\n} from '@/components/ai-elements/conversation';\nimport {\n  Message,\n  MessageContent,\n  MessageResponse,\n} from '@/components/ai-elements/message';\nimport {\n  PromptInput,\n  PromptInputButton,\n  PromptInputFooter,\n  PromptInputSubmit,\n  PromptInputTextarea,\n  PromptInputTools,\n} from '@/components/ai-elements/prompt-input';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\ninterface DemoMessage {\n  id: string;\n  role: 'user' | 'assistant';\n  content: string;\n}\n\nconst INITIAL_MESSAGES: DemoMessage[] = [\n  {\n    id: 'intro',\n    role: 'assistant',\n    content:\n      'Hi, I am your workspace assistant. I can draft copy, summarize documents, and turn notes into tasks. What are you working on?',\n  },\n  {\n    id: 'question',\n    role: 'user',\n    content: 'Summarize the Q3 launch plan in three bullets.',\n  },\n  {\n    id: 'answer',\n    role: 'assistant',\n    content:\n      '- **Ship the new onboarding** by Sept 18 with an A/B test on pricing.\\n- **Open EU and APAC** with localized checkout.\\n- **Launch the partner API** in private beta with 12 partners.',\n  },\n];\n\nconst SUGGESTIONS = [\n  'Draft a launch announcement',\n  'List open risks',\n  'Turn this into tasks',\n];\n\nconst RESPONSES = [\n  'Here is a quick outline you can reuse:\\n\\n1. Swap the mock response with a real API call.\\n2. Stream tokens into `MessageResponse`.\\n3. Keep the layout as-is for a consistent UI.',\n  'If you want multi-model support, add a small model selector to the prompt footer and pass the selection to your backend.',\n  'You can also inject tools like file upload or voice input by adding buttons to the prompt footer.',\n];\n\nexport default function Ai05() {\n  const [messages, setMessages] = useState<DemoMessage[]>(INITIAL_MESSAGES);\n  const [inputValue, setInputValue] = useState('');\n  const [status, setStatus] = useState<ChatStatus>('ready');\n\n  const handleSend = (text: string) => {\n    const trimmed = text.trim();\n    if (!trimmed || status !== 'ready') {\n      return;\n    }\n\n    setMessages((prev) => [\n      ...prev,\n      { id: `user-${Date.now()}`, role: 'user', content: trimmed },\n    ]);\n    setInputValue('');\n    setStatus('submitted');\n\n    window.setTimeout(() => {\n      setMessages((prev) => [\n        ...prev,\n        {\n          id: `assistant-${Date.now()}`,\n          role: 'assistant',\n          content: RESPONSES[messages.length % RESPONSES.length],\n        },\n      ]);\n      setStatus('ready');\n    }, 1200);\n  };\n\n  const handleReset = () => {\n    setMessages(INITIAL_MESSAGES.slice(0, 1));\n    setInputValue('');\n    setStatus('ready');\n  };\n\n  return (\n    <div className=\"w-full px-4\">\n      <div className=\"mx-auto flex h-[560px] w-full max-w-2xl flex-col overflow-hidden rounded-3xl bg-card shadow-[0_0_0_1px_oklch(0_0_0/0.06),0_1px_2px_oklch(0_0_0/0.04),0_12px_32px_-12px_oklch(0_0_0/0.18)] dark:shadow-[0_0_0_1px_oklch(1_0_0/0.1),0_12px_32px_-12px_oklch(0_0_0/0.6)]\">\n        <header className=\"flex items-center justify-between gap-4 px-5 py-4\">\n          <div className=\"flex flex-col\">\n            <span className=\"font-semibold text-sm leading-tight\">\n              Assistant\n            </span>\n            <span className=\"inline-flex items-center gap-1.5 text-muted-foreground text-xs\">\n              <span className=\"size-1.5 rounded-full bg-emerald-500\" />\n              Online\n            </span>\n          </div>\n          <div className=\"flex items-center gap-1\">\n            <Button\n              className=\"h-8 gap-1.5 px-2.5 text-xs\"\n              onClick={handleReset}\n              size=\"sm\"\n              variant=\"ghost\"\n            >\n              <IconPlus className=\"size-4\" stroke={1.5} />\n              New chat\n            </Button>\n            <Button\n              aria-label=\"Settings\"\n              className=\"size-8\"\n              size=\"icon\"\n              title=\"Settings\"\n              variant=\"ghost\"\n            >\n              <IconAdjustmentsHorizontal className=\"size-4\" stroke={1.5} />\n            </Button>\n          </div>\n        </header>\n\n        <Conversation className=\"border-border/60 border-t\">\n          <ConversationContent className=\"gap-5 px-5 py-5\">\n            {messages.map((message) => (\n              <Message from={message.role} key={message.id}>\n                <MessageContent\n                  className={cn(\n                    'leading-relaxed',\n                    message.role === 'assistant' && 'max-w-prose',\n                    message.role === 'user' &&\n                      'group-[.is-user]:rounded-2xl group-[.is-user]:rounded-br-md group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground'\n                  )}\n                >\n                  {message.role === 'assistant' ? (\n                    <MessageResponse>{message.content}</MessageResponse>\n                  ) : (\n                    <p className=\"whitespace-pre-wrap text-pretty\">\n                      {message.content}\n                    </p>\n                  )}\n                </MessageContent>\n              </Message>\n            ))}\n            {status === 'submitted' && (\n              <Message from=\"assistant\">\n                <output\n                  aria-label=\"Assistant is typing\"\n                  className=\"flex h-7 items-center gap-1\"\n                >\n                  {[0, 1, 2].map((dot) => (\n                    <span\n                      className=\"size-1.5 animate-bounce rounded-full bg-muted-foreground/60\"\n                      key={dot}\n                      style={{ animationDelay: `${dot * 150}ms` }}\n                    />\n                  ))}\n                </output>\n              </Message>\n            )}\n          </ConversationContent>\n          <ConversationScrollButton />\n        </Conversation>\n\n        <div className=\"flex flex-col gap-3 p-3\">\n          <div className=\"flex flex-wrap gap-2 px-1\">\n            {SUGGESTIONS.map((suggestion) => (\n              <button\n                className=\"rounded-full border border-border/70 bg-background px-3 py-1.5 text-muted-foreground text-xs transition-[color,background-color,scale] hover:bg-muted hover:text-foreground active:scale-[0.96] disabled:pointer-events-none disabled:opacity-50\"\n                disabled={status !== 'ready'}\n                key={suggestion}\n                onClick={() => handleSend(suggestion)}\n                type=\"button\"\n              >\n                {suggestion}\n              </button>\n            ))}\n          </div>\n          <PromptInput\n            className=\"[&>[data-slot=input-group]]:rounded-xl [&>[data-slot=input-group]]:border-border [&>[data-slot=input-group]]:bg-muted/40 [&>[data-slot=input-group]]:shadow-none\"\n            onSubmit={(message) => handleSend(message.text)}\n          >\n            <PromptInputTextarea\n              onChange={(event) => setInputValue(event.currentTarget.value)}\n              placeholder=\"Message Assistant...\"\n              value={inputValue}\n            />\n            <PromptInputFooter>\n              <PromptInputTools>\n                <PromptInputButton aria-label=\"Attach file\">\n                  <IconPaperclip className=\"size-4\" stroke={1.5} />\n                </PromptInputButton>\n                <PromptInputButton aria-label=\"Quick prompt\">\n                  <IconBolt className=\"size-4\" stroke={1.5} />\n                </PromptInputButton>\n              </PromptInputTools>\n              <PromptInputSubmit\n                className=\"rounded-full transition-[scale,opacity] active:scale-[0.96]\"\n                disabled={!inputValue.trim() || status !== 'ready'}\n                status={status}\n              />\n            </PromptInputFooter>\n          </PromptInput>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "ai"
      ]
    },
    {
      "name": "chat-01",
      "type": "registry:block",
      "title": "Chat Basic",
      "description": "A chat basic block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "bubble",
        "button",
        "dropdown-menu",
        "input-group",
        "message",
        "message-scroller",
        "textarea"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/chat/chat-01.tsx",
          "type": "registry:component",
          "target": "components/chat-01.tsx",
          "content": "'use client';\n\nimport {\n  IconArrowUp,\n  IconCopy,\n  IconMicrophone,\n  IconPaperclip,\n  IconPhoto,\n  IconPlayerStopFilled,\n  IconPlus,\n  IconRefresh,\n  IconThumbDown,\n  IconThumbUp,\n  IconWorldSearch,\n} from '@tabler/icons-react';\nimport type React from 'react';\nimport { useState } from 'react';\nimport { Bubble, BubbleContent } from '@/components/ui/bubble';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupInput,\n} from '@/components/ui/input-group';\nimport {\n  Message,\n  MessageContent,\n  MessageFooter,\n} from '@/components/ui/message';\nimport {\n  MessageScroller,\n  MessageScrollerButton,\n  MessageScrollerContent,\n  MessageScrollerItem,\n  MessageScrollerProvider,\n  MessageScrollerViewport,\n} from '@/components/ui/message-scroller';\nimport { Textarea } from '@/components/ui/textarea';\nimport { cn } from '@/lib/utils';\n\ntype Turn = {\n  id: string;\n  role: 'user' | 'assistant';\n  text: string;\n  streaming?: boolean;\n};\n\nconst thread: Turn[] = [\n  {\n    id: '1',\n    role: 'user',\n    text: 'Is it true that a day on Venus is longer than its year?',\n  },\n  {\n    id: '2',\n    role: 'assistant',\n    text: 'Yes. Venus takes 243 Earth days to rotate once, but only 225 days to orbit the Sun, so its day is longer than its year.\\n\\nIt also spins in the opposite direction to most planets, which means the Sun rises in the west there.',\n  },\n  { id: '3', role: 'user', text: 'How hot does it get on the surface?' },\n  {\n    id: '4',\n    role: 'assistant',\n    text: 'The surface sits around 465°C, hot enough to melt lead. The atmosphere traps so much heat that Venus stays hotter than Mercury, even though Mercury is much closer to the Sun.',\n  },\n];\n\nconst replies = [\n  'Good question. Venus has no moons and no rings, which makes it unusual among the inner planets. Most explanations point to its slow, retrograde spin: any moon it once had would have drifted inward and been pulled apart by tidal forces.\\n\\nMercury is the only other moonless planet in the solar system.',\n  'The clouds are mostly sulfuric acid droplets, sitting about 50 to 70 km above the surface. They reflect roughly 75% of incoming sunlight, which is why Venus is the brightest object in our sky after the Moon.\\n\\nBelow the clouds the air is almost entirely carbon dioxide, with pressure about 92 times that of Earth at sea level.',\n  'Several landers have made it down. The Soviet Venera 7 was the first to transmit from the surface in 1970, and Venera 13 survived for 127 minutes in 1982, long enough to send back the first color photos.\\n\\nNothing has lasted more than a couple of hours; the heat and pressure destroy electronics quickly.',\n];\n\nconst thinkDelay = 600;\nconst wordDelay = 35;\n\nconst easeOut = 'ease-[cubic-bezier(0.23,1,0.32,1)]';\nconst press = `${easeOut} transition-[scale,background-color] duration-150 active:scale-[0.96]`;\nconst enter = `${easeOut} motion-reduce:slide-in-from-bottom-0 fade-in slide-in-from-bottom-1 animate-in duration-200`;\nconst swap = `${easeOut} fade-in zoom-in-95 animate-in duration-150`;\n\nexport default function Chat01() {\n  const [turns, setTurns] = useState(thread);\n  const [draft, setDraft] = useState('');\n  const [listening, setListening] = useState(false);\n  const [replyIndex, setReplyIndex] = useState(0);\n  const [edit, setEdit] = useState<{ id: string; text: string } | null>(null);\n  const streaming = turns.some((t) => t.streaming);\n\n  const stream = (replyId: string) => {\n    const words = replies[replyIndex % replies.length].split(' ');\n    setReplyIndex(replyIndex + 1);\n    setTurns((prev) =>\n      prev.map((t) =>\n        t.id === replyId ? { ...t, text: '', streaming: true } : t\n      )\n    );\n    let shown = 0;\n    setTimeout(() => {\n      const timer = setInterval(() => {\n        shown += 1;\n        const done = shown >= words.length;\n        setTurns((prev) =>\n          prev.map((t) =>\n            t.id === replyId\n              ? {\n                  ...t,\n                  text: words.slice(0, shown).join(' '),\n                  streaming: !done,\n                }\n              : t\n          )\n        );\n        if (done) {\n          clearInterval(timer);\n        }\n      }, wordDelay);\n    }, thinkDelay);\n  };\n\n  const send = (e: React.FormEvent) => {\n    e.preventDefault();\n    const text = draft.trim();\n    if (!text || streaming) {\n      return;\n    }\n    const replyId = crypto.randomUUID();\n    setTurns([\n      ...turns,\n      { id: crypto.randomUUID(), role: 'user', text },\n      { id: replyId, role: 'assistant', text: '' },\n    ]);\n    setDraft('');\n    stream(replyId);\n  };\n\n  const resend = () => {\n    const text = edit?.text.trim();\n    if (!(edit && text) || streaming) {\n      return;\n    }\n    const at = turns.findIndex((t) => t.id === edit.id);\n    const replyId = crypto.randomUUID();\n    setTurns([\n      ...turns.slice(0, at),\n      { id: edit.id, role: 'user', text },\n      { id: replyId, role: 'assistant', text: '' },\n    ]);\n    setEdit(null);\n    stream(replyId);\n  };\n\n  const editLast = (e: React.KeyboardEvent) => {\n    const last = turns.findLast((t) => t.role === 'user');\n    if (e.key === 'ArrowUp' && !draft && last) {\n      e.preventDefault();\n      setEdit({ id: last.id, text: last.text });\n    }\n  };\n\n  const body = (turn: Turn) => {\n    const mine = turn.role === 'user';\n    if (edit?.id === turn.id) {\n      return (\n        <>\n          <Textarea\n            aria-label=\"Edit message\"\n            autoFocus\n            className={cn(\n              enter,\n              'min-h-0 w-md max-w-full resize-none rounded-[22px] border-0 bg-muted px-4 py-3 font-medium text-[15px]/6 focus-visible:ring-0 md:text-[15px]/6'\n            )}\n            onChange={(e) => setEdit({ ...edit, text: e.target.value })}\n            onKeyDown={(e) => {\n              if (e.key === 'Escape') {\n                setEdit(null);\n              }\n              if (e.key === 'Enter' && !e.shiftKey) {\n                e.preventDefault();\n                resend();\n              }\n            }}\n            value={edit.text}\n          />\n          <div className={cn(enter, 'flex items-center gap-1.5 self-end')}>\n            <Button\n              aria-label=\"Copy\"\n              className={cn(press, 'rounded-full')}\n              onClick={() => navigator.clipboard.writeText(turn.text)}\n              size=\"icon-sm\"\n              variant=\"ghost\"\n            >\n              <IconCopy stroke={1.6} />\n            </Button>\n            <Button\n              className={cn(press, 'rounded-full')}\n              onClick={() => setEdit(null)}\n              size=\"sm\"\n              variant=\"secondary\"\n            >\n              Cancel\n            </Button>\n            <Button\n              className={cn(press, 'rounded-full')}\n              disabled={!edit.text.trim() || streaming}\n              onClick={resend}\n              size=\"sm\"\n            >\n              Send\n            </Button>\n          </div>\n        </>\n      );\n    }\n    if (turn.streaming && !turn.text) {\n      return (\n        <span className=\"shimmer text-[15px]/6 text-muted-foreground\">\n          Thinking…\n        </span>\n      );\n    }\n    return turn.text.split('\\n\\n').map((paragraph) => (\n      <Bubble\n        align={mine ? 'end' : 'start'}\n        className={\n          mine\n            ? '-mx-2.5 -my-1 box-content max-w-md cursor-pointer rounded-xl px-2.5 py-1 text-right font-medium transition-colors duration-150 ease-out hover:bg-muted'\n            : undefined\n        }\n        key={paragraph}\n        onDoubleClick={\n          mine ? () => setEdit({ id: turn.id, text: turn.text }) : undefined\n        }\n        title={mine ? 'Double-click to edit' : undefined}\n        variant=\"ghost\"\n      >\n        <BubbleContent className=\"text-[15px]/6\">{paragraph}</BubbleContent>\n      </Bubble>\n    ));\n  };\n\n  return (\n    <div className=\"flex h-dvh w-full flex-col bg-background\">\n      <MessageScrollerProvider autoScroll>\n        <MessageScroller>\n          <MessageScrollerViewport>\n            <MessageScrollerContent className=\"mx-auto w-full max-w-2xl gap-3.5 px-1.5 pt-12 pb-4\">\n              {turns.map((turn) => {\n                const mine = turn.role === 'user';\n                return (\n                  <MessageScrollerItem\n                    className={cn(\n                      'px-2.5',\n                      mine && 'pt-5.5 pb-1 first:pt-1',\n                      !thread.some((t) => t.id === turn.id) && enter\n                    )}\n                    key={turn.id}\n                    messageId={turn.id}\n                  >\n                    <Message align={mine ? 'end' : 'start'}>\n                      <MessageContent className=\"gap-2\">\n                        {body(turn)}\n                        {!(mine || turn.streaming) && (\n                          <MessageFooter className={cn(enter, 'gap-0.5')}>\n                            <Button\n                              aria-label=\"Copy\"\n                              className={press}\n                              onClick={() =>\n                                navigator.clipboard.writeText(turn.text)\n                              }\n                              size=\"icon-sm\"\n                              variant=\"ghost\"\n                            >\n                              <IconCopy stroke={1.6} />\n                            </Button>\n                            <Button\n                              aria-label=\"Good response\"\n                              className={press}\n                              size=\"icon-sm\"\n                              variant=\"ghost\"\n                            >\n                              <IconThumbUp stroke={1.6} />\n                            </Button>\n                            <Button\n                              aria-label=\"Bad response\"\n                              className={press}\n                              size=\"icon-sm\"\n                              variant=\"ghost\"\n                            >\n                              <IconThumbDown stroke={1.6} />\n                            </Button>\n                            <Button\n                              aria-label=\"Regenerate\"\n                              className={press}\n                              onClick={() => stream(turn.id)}\n                              size=\"icon-sm\"\n                              variant=\"ghost\"\n                            >\n                              <IconRefresh stroke={1.6} />\n                            </Button>\n                          </MessageFooter>\n                        )}\n                      </MessageContent>\n                    </Message>\n                  </MessageScrollerItem>\n                );\n              })}\n            </MessageScrollerContent>\n          </MessageScrollerViewport>\n          <MessageScrollerButton className={easeOut} />\n        </MessageScroller>\n      </MessageScrollerProvider>\n\n      <form\n        className=\"mx-auto flex w-full max-w-2xl flex-col items-center gap-2.5 px-4 pt-3 pb-4\"\n        onSubmit={send}\n      >\n        <InputGroup className=\"h-13 rounded-full border-0 bg-muted px-2.5 has-[[data-slot=input-group-control]:focus-visible]:ring-0\">\n          <InputGroupAddon>\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <InputGroupButton\n                    aria-label=\"Add attachment\"\n                    className={cn(press, 'rounded-full hover:bg-foreground/10')}\n                    size=\"icon-sm\"\n                  />\n                }\n              >\n                <IconPlus stroke={1.8} />\n              </DropdownMenuTrigger>\n              <DropdownMenuContent\n                align=\"start\"\n                className=\"w-60 rounded-[20px] p-2 shadow-[0_0_0_1px_oklch(0_0_0/0.06),0_8px_24px_-6px_oklch(0_0_0/0.14)]\"\n                sideOffset={10}\n              >\n                <DropdownMenuGroup className=\"flex flex-col gap-0.5\">\n                  <DropdownMenuItem className=\"gap-3 rounded-xl px-3 py-2.5 text-[15px] [&>svg]:text-muted-foreground\">\n                    <IconPaperclip className=\"size-4.5\" stroke={1.6} />\n                    Add files\n                  </DropdownMenuItem>\n                  <DropdownMenuItem className=\"gap-3 rounded-xl px-3 py-2.5 text-[15px] [&>svg]:text-muted-foreground\">\n                    <IconPhoto className=\"size-4.5\" stroke={1.6} />\n                    Add photos\n                  </DropdownMenuItem>\n                  <DropdownMenuItem className=\"gap-3 rounded-xl px-3 py-2.5 text-[15px] [&>svg]:text-muted-foreground\">\n                    <IconWorldSearch className=\"size-4.5\" stroke={1.6} />\n                    Search the web\n                  </DropdownMenuItem>\n                </DropdownMenuGroup>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </InputGroupAddon>\n          <InputGroupInput\n            className=\"text-[15px] md:text-[15px]\"\n            onChange={(e) => setDraft(e.target.value)}\n            onKeyDown={editLast}\n            placeholder={listening ? 'Listening…' : 'Ask anything'}\n            value={draft}\n          />\n          <InputGroupAddon align=\"inline-end\">\n            {draft.trim() ? (\n              <InputGroupButton\n                aria-label=\"Send\"\n                className={cn(swap, press, 'rounded-full')}\n                disabled={streaming}\n                size=\"icon-sm\"\n                type=\"submit\"\n                variant=\"default\"\n              >\n                <IconArrowUp stroke={2} />\n              </InputGroupButton>\n            ) : (\n              <InputGroupButton\n                aria-label={listening ? 'Stop listening' : 'Voice input'}\n                aria-pressed={listening}\n                className={cn(\n                  swap,\n                  press,\n                  'rounded-full',\n                  listening\n                    ? 'bg-destructive/15 text-destructive hover:bg-destructive/25'\n                    : 'hover:bg-foreground/10'\n                )}\n                onClick={() => setListening(!listening)}\n                size=\"icon-sm\"\n              >\n                {listening ? (\n                  <IconPlayerStopFilled className=\"animate-pulse motion-reduce:animate-none\" />\n                ) : (\n                  <IconMicrophone stroke={1.8} />\n                )}\n              </InputGroupButton>\n            )}\n          </InputGroupAddon>\n        </InputGroup>\n        <p className=\"text-muted-foreground text-xs\">\n          AI can make mistakes. Check important info.\n        </p>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "chat"
      ]
    },
    {
      "name": "chat-02",
      "type": "registry:block",
      "title": "Chat with Model Menu",
      "description": "A chat with model menu block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "bubble",
        "button",
        "dropdown-menu",
        "input-group",
        "message",
        "message-scroller",
        "textarea"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/chat/chat-02.tsx",
          "type": "registry:component",
          "target": "components/chat-02.tsx",
          "content": "'use client';\n\nimport {\n  IconArrowUp,\n  IconChevronDown,\n  IconCopy,\n  IconDots,\n  IconMicrophone,\n  IconPaperclip,\n  IconPencil,\n  IconPhoto,\n  IconPlayerStopFilled,\n  IconPlus,\n  IconRefresh,\n  IconThumbDown,\n  IconThumbUp,\n  IconVolume,\n  IconWorldSearch,\n} from '@tabler/icons-react';\nimport type React from 'react';\nimport { useState } from 'react';\nimport { Bubble, BubbleContent } from '@/components/ui/bubble';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupTextarea,\n} from '@/components/ui/input-group';\nimport {\n  Message,\n  MessageContent,\n  MessageFooter,\n} from '@/components/ui/message';\nimport {\n  MessageScroller,\n  MessageScrollerButton,\n  MessageScrollerContent,\n  MessageScrollerItem,\n  MessageScrollerProvider,\n  MessageScrollerViewport,\n} from '@/components/ui/message-scroller';\nimport { Textarea } from '@/components/ui/textarea';\nimport { cn } from '@/lib/utils';\n\ntype Turn = {\n  id: string;\n  role: 'user' | 'assistant';\n  text: string;\n  streaming?: boolean;\n};\n\nconst thread: Turn[] = [\n  {\n    id: '1',\n    role: 'user',\n    text: 'Is it true that a day on Venus is longer than its year?',\n  },\n  {\n    id: '2',\n    role: 'assistant',\n    text: 'Yes. Venus takes 243 Earth days to rotate once, but only 225 days to orbit the Sun, so its day is longer than its year.\\n\\nIt also spins in the opposite direction to most planets, which means the Sun rises in the west there.',\n  },\n  { id: '3', role: 'user', text: 'How hot does it get on the surface?' },\n  {\n    id: '4',\n    role: 'assistant',\n    text: 'The surface sits around 465°C, hot enough to melt lead. The atmosphere traps so much heat that Venus stays hotter than Mercury, even though Mercury is much closer to the Sun.',\n  },\n  {\n    id: '5',\n    role: 'user',\n    text: 'How long would a person survive there without a suit?',\n  },\n  {\n    id: '6',\n    role: 'assistant',\n    text: \"A few seconds at most. The pressure down there is 92 times Earth's, similar to being 900 meters underwater, and the heat alone would be fatal almost immediately.\\n\\nFor reference, the Soviet Venera landers were built for those conditions and still failed within about two hours.\",\n  },\n];\n\nconst replies = [\n  'Good question. Venus has no moons and no rings, which makes it unusual among the inner planets. Most explanations point to its slow, retrograde spin: any moon it once had would have drifted inward and been pulled apart by tidal forces.\\n\\nMercury is the only other moonless planet in the solar system.',\n  'The clouds are mostly sulfuric acid droplets, sitting about 50 to 70 km above the surface. They reflect roughly 75% of incoming sunlight, which is why Venus is the brightest object in our sky after the Moon.\\n\\nBelow the clouds the air is almost entirely carbon dioxide, with pressure about 92 times that of Earth at sea level.',\n  'Several landers have made it down. The Soviet Venera 7 was the first to transmit from the surface in 1970, and Venera 13 survived for 127 minutes in 1982, long enough to send back the first color photos.\\n\\nNothing has lasted more than a couple of hours; the heat and pressure destroy electronics quickly.',\n];\n\nconst levels = [\n  { name: 'Instant', hint: '5.5' },\n  { name: 'Medium' },\n  { name: 'High' },\n  { name: 'Extra High' },\n  { name: 'Pro' },\n];\n\nconst models = [\n  'GPT-6',\n  'GPT-5.6 Sol',\n  'GPT-6 Astra',\n  'GPT-5.6 Terra',\n  'GPT-5.6 Luna',\n];\n\nconst thinkDelay = 600;\nconst wordDelay = 35;\n\nconst easeOut = 'ease-[cubic-bezier(0.23,1,0.32,1)]';\nconst press = `${easeOut} transition-[scale,background-color] duration-150 active:scale-[0.96] motion-reduce:active:scale-100`;\nconst enter = `${easeOut} motion-reduce:slide-in-from-bottom-0 fade-in slide-in-from-bottom-1 animate-in duration-200`;\nconst swap = `${easeOut} fade-in zoom-in-95 motion-reduce:zoom-in-100 animate-in duration-150`;\nconst item = 'rounded-lg px-2.5 py-1.75 text-[13px]';\n\nexport default function Chat02() {\n  const [turns, setTurns] = useState(thread);\n  const [draft, setDraft] = useState('');\n  const [tall, setTall] = useState(false);\n  const [listening, setListening] = useState(false);\n  const [level, setLevel] = useState('Extra High');\n  const [model, setModel] = useState('GPT-5.6 Sol');\n  const [replyIndex, setReplyIndex] = useState(0);\n  const [edit, setEdit] = useState<{ id: string; text: string } | null>(null);\n  const streaming = turns.some((t) => t.streaming);\n\n  const stream = (replyId: string) => {\n    const words = replies[replyIndex % replies.length].split(' ');\n    setReplyIndex(replyIndex + 1);\n    setTurns((prev) =>\n      prev.map((t) =>\n        t.id === replyId ? { ...t, text: '', streaming: true } : t\n      )\n    );\n    let shown = 0;\n    setTimeout(() => {\n      const timer = setInterval(() => {\n        shown += 1;\n        const done = shown >= words.length;\n        setTurns((prev) =>\n          prev.map((t) =>\n            t.id === replyId\n              ? {\n                  ...t,\n                  text: words.slice(0, shown).join(' '),\n                  streaming: !done,\n                }\n              : t\n          )\n        );\n        if (done) {\n          clearInterval(timer);\n        }\n      }, wordDelay);\n    }, thinkDelay);\n  };\n\n  const send = (e: React.FormEvent) => {\n    e.preventDefault();\n    const text = draft.trim();\n    if (!text || streaming) {\n      return;\n    }\n    const replyId = crypto.randomUUID();\n    setTurns([\n      ...turns,\n      { id: crypto.randomUUID(), role: 'user', text },\n      { id: replyId, role: 'assistant', text: '' },\n    ]);\n    setDraft('');\n    setTall(false);\n    stream(replyId);\n  };\n\n  const resend = () => {\n    const text = edit?.text.trim();\n    if (!(edit && text) || streaming) {\n      return;\n    }\n    const at = turns.findIndex((t) => t.id === edit.id);\n    const reply = turns[at + 1];\n    setTurns(turns.map((t) => (t.id === edit.id ? { ...t, text } : t)));\n    setEdit(null);\n    if (reply?.role === 'assistant') {\n      stream(reply.id);\n    }\n  };\n\n  const body = (turn: Turn, align: 'start' | 'end') => {\n    if (edit?.id === turn.id) {\n      return (\n        <>\n          <Textarea\n            aria-label=\"Edit message\"\n            autoFocus\n            className={cn(\n              enter,\n              'min-h-0 w-md max-w-full resize-none rounded-[18px] border-0 bg-muted px-3.5 py-2.5 text-[15px]/6 focus-visible:ring-0 md:text-[15px]/6'\n            )}\n            onChange={(e) => setEdit({ ...edit, text: e.target.value })}\n            onKeyDown={(e) => {\n              if (e.key === 'Escape') {\n                setEdit(null);\n              }\n              if (e.key === 'Enter' && !e.shiftKey) {\n                e.preventDefault();\n                resend();\n              }\n            }}\n            value={edit.text}\n          />\n          <div className={cn(enter, 'flex items-center gap-1.5 self-end')}>\n            <Button\n              className={cn(press, 'rounded-full')}\n              onClick={() => setEdit(null)}\n              size=\"sm\"\n              variant=\"secondary\"\n            >\n              Cancel\n            </Button>\n            <Button\n              className={cn(press, 'rounded-full')}\n              disabled={!edit.text.trim() || streaming}\n              onClick={resend}\n              size=\"sm\"\n            >\n              Send\n            </Button>\n          </div>\n        </>\n      );\n    }\n    if (turn.streaming && !turn.text) {\n      return (\n        <span className=\"shimmer text-[15px]/6 text-muted-foreground\">\n          Thinking…\n        </span>\n      );\n    }\n    return turn.text.split('\\n\\n').map((paragraph) => (\n      <Bubble\n        align={align}\n        className=\"max-w-md data-[variant=ghost]:max-w-full\"\n        key={paragraph}\n        variant={turn.role === 'user' ? 'muted' : 'ghost'}\n      >\n        <BubbleContent className=\"rounded-[18px] px-3.5 py-1.75 text-[15px]/5.5 group-data-[variant=ghost]/bubble:text-[15px]/6\">\n          {paragraph}\n        </BubbleContent>\n      </Bubble>\n    ));\n  };\n\n  const action = (\n    label: string,\n    icon: React.ReactNode,\n    onClick?: () => void\n  ) => (\n    <Button\n      aria-label={label}\n      className={cn(press, 'rounded-lg')}\n      onClick={onClick}\n      size=\"icon-sm\"\n      variant=\"ghost\"\n    >\n      {icon}\n    </Button>\n  );\n\n  return (\n    <div className=\"flex h-dvh w-full flex-col bg-background\">\n      <MessageScrollerProvider autoScroll>\n        <MessageScroller>\n          <MessageScrollerViewport>\n            <MessageScrollerContent className=\"mx-auto w-full max-w-2xl gap-4.5 px-4 pt-10 pb-4\">\n              {turns.map((turn) => {\n                const mine = turn.role === 'user';\n                const align = mine ? 'end' : 'start';\n                return (\n                  <MessageScrollerItem\n                    className={cn(\n                      mine && 'pt-4.5 first:pt-0',\n                      !thread.some((t) => t.id === turn.id) && enter\n                    )}\n                    key={turn.id}\n                    messageId={turn.id}\n                  >\n                    <Message align={align}>\n                      <MessageContent className=\"gap-2.5\">\n                        {body(turn, align)}\n                        {turn.text && edit?.id !== turn.id && (\n                          <MessageFooter className=\"-mx-2 gap-0.5\">\n                            {action('Copy', <IconCopy stroke={1.6} />, () =>\n                              navigator.clipboard.writeText(turn.text)\n                            )}\n                            {mine\n                              ? action(\n                                  'Edit',\n                                  <IconPencil stroke={1.6} />,\n                                  () =>\n                                    setEdit({ id: turn.id, text: turn.text })\n                                )\n                              : action(\n                                  'Regenerate',\n                                  <IconRefresh stroke={1.6} />,\n                                  () => stream(turn.id)\n                                )}\n                            {!mine && (\n                              <DropdownMenu>\n                                <DropdownMenuTrigger\n                                  render={action(\n                                    'More',\n                                    <IconDots stroke={1.6} />\n                                  )}\n                                />\n                                <DropdownMenuContent\n                                  align=\"start\"\n                                  className=\"w-44 rounded-[14px] p-1.5\"\n                                >\n                                  <DropdownMenuGroup className=\"flex flex-col gap-0.5\">\n                                    <DropdownMenuItem\n                                      className={cn(item, 'gap-2.5')}\n                                    >\n                                      <IconThumbUp stroke={1.6} />\n                                      Good response\n                                    </DropdownMenuItem>\n                                    <DropdownMenuItem\n                                      className={cn(item, 'gap-2.5')}\n                                    >\n                                      <IconThumbDown stroke={1.6} />\n                                      Bad response\n                                    </DropdownMenuItem>\n                                    <DropdownMenuItem\n                                      className={cn(item, 'gap-2.5')}\n                                    >\n                                      <IconVolume stroke={1.6} />\n                                      Read aloud\n                                    </DropdownMenuItem>\n                                  </DropdownMenuGroup>\n                                </DropdownMenuContent>\n                              </DropdownMenu>\n                            )}\n                          </MessageFooter>\n                        )}\n                      </MessageContent>\n                    </Message>\n                  </MessageScrollerItem>\n                );\n              })}\n            </MessageScrollerContent>\n          </MessageScrollerViewport>\n          <MessageScrollerButton\n            className={cn(\n              easeOut,\n              'bottom-2 h-7 gap-1.5 rounded-full border-0 px-3.5 font-medium text-muted-foreground text-xs shadow-[0_0_0_1px_oklch(0_0_0/0.06),0_2px_8px_oklch(0_0_0/0.08)] transition-[translate,scale,opacity,background-color] hover:text-foreground active:not-aria-[haspopup]:translate-y-0 data-[active=true]:active:scale-[0.96] dark:shadow-[0_0_0_1px_oklch(1_0_0/0.1),0_2px_8px_oklch(0_0_0/0.3)]'\n            )}\n            size=\"sm\"\n          >\n            <IconChevronDown className=\"size-3.5\" stroke={1.75} />\n            Jump to latest\n          </MessageScrollerButton>\n        </MessageScroller>\n      </MessageScrollerProvider>\n\n      <form\n        className=\"mx-auto flex w-full max-w-2xl flex-col items-center gap-2.5 px-4 pt-2 pb-3.5\"\n        onSubmit={send}\n      >\n        <InputGroup\n          className={cn(\n            'min-h-11 items-end rounded-[22px] bg-background px-0.5 shadow-xs has-[[data-slot=input-group-control]:focus-visible]:ring-0',\n            tall && 'flex-wrap rounded-3xl'\n          )}\n        >\n          <InputGroupAddon\n            className={cn('pb-1.25 pl-2.5', tall && 'order-none')}\n          >\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <InputGroupButton\n                    aria-label=\"Add attachment\"\n                    className={cn(press, 'rounded-full')}\n                    size=\"icon-sm\"\n                  />\n                }\n              >\n                <IconPlus stroke={1.8} />\n              </DropdownMenuTrigger>\n              <DropdownMenuContent\n                align=\"start\"\n                className=\"w-56 rounded-[14px] p-1.5\"\n                sideOffset={10}\n              >\n                <DropdownMenuGroup className=\"flex flex-col gap-0.5\">\n                  <DropdownMenuItem className={cn(item, 'gap-2.5')}>\n                    <IconPaperclip stroke={1.6} />\n                    Add files\n                  </DropdownMenuItem>\n                  <DropdownMenuItem className={cn(item, 'gap-2.5')}>\n                    <IconPhoto stroke={1.6} />\n                    Add photos\n                  </DropdownMenuItem>\n                  <DropdownMenuItem className={cn(item, 'gap-2.5')}>\n                    <IconWorldSearch stroke={1.6} />\n                    Search the web\n                  </DropdownMenuItem>\n                </DropdownMenuGroup>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </InputGroupAddon>\n          <InputGroupTextarea\n            className={cn(\n              'max-h-48 min-h-0 py-2.5 pl-1 text-[15px]/6 md:text-[15px]/6',\n              tall && '-order-1 basis-full px-2.75 py-1.5'\n            )}\n            onChange={(e) => {\n              setDraft(e.target.value);\n              setTall(e.target.scrollHeight > 44);\n            }}\n            onKeyDown={(e) => {\n              if (e.key === 'Enter' && !e.shiftKey) {\n                e.preventDefault();\n                e.currentTarget.form?.requestSubmit();\n              }\n            }}\n            placeholder={listening ? 'Listening…' : 'Ask anything'}\n            rows={1}\n            value={draft}\n          />\n          <InputGroupAddon\n            align=\"inline-end\"\n            className={cn('gap-1.5 pr-2.5 pb-1.25', tall && 'ml-auto')}\n          >\n            {draft.trim() ? (\n              <InputGroupButton\n                aria-label=\"Send\"\n                className={cn(swap, press, 'rounded-full')}\n                disabled={streaming}\n                size=\"icon-sm\"\n                type=\"submit\"\n                variant=\"default\"\n              >\n                <IconArrowUp stroke={2} />\n              </InputGroupButton>\n            ) : (\n              <InputGroupButton\n                aria-label={listening ? 'Stop listening' : 'Voice input'}\n                aria-pressed={listening}\n                className={cn(\n                  swap,\n                  press,\n                  'rounded-full',\n                  listening &&\n                    'bg-destructive/15 text-destructive hover:bg-destructive/25'\n                )}\n                onClick={() => setListening(!listening)}\n                size=\"icon-sm\"\n              >\n                {listening ? (\n                  <IconPlayerStopFilled className=\"animate-pulse motion-reduce:animate-none\" />\n                ) : (\n                  <IconMicrophone stroke={1.8} />\n                )}\n              </InputGroupButton>\n            )}\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <InputGroupButton\n                    aria-label=\"Intelligence level\"\n                    className={cn(\n                      press,\n                      'h-8 gap-1.25 rounded-full bg-muted px-3 font-medium text-[13px] text-muted-foreground hover:bg-muted'\n                    )}\n                    size=\"sm\"\n                  />\n                }\n              >\n                {level}\n                <IconChevronDown className=\"size-3\" stroke={2} />\n              </DropdownMenuTrigger>\n              <DropdownMenuContent\n                align=\"end\"\n                className=\"w-49 rounded-[14px] p-1.5\"\n                side=\"top\"\n                sideOffset={10}\n              >\n                <DropdownMenuGroup className=\"flex flex-col gap-0.5\">\n                  <DropdownMenuLabel className=\"px-2.5 pt-1 pb-0.5 text-[11px]\">\n                    Intelligence\n                  </DropdownMenuLabel>\n                  <DropdownMenuRadioGroup\n                    onValueChange={setLevel}\n                    value={level}\n                  >\n                    {levels.map((l) => (\n                      <DropdownMenuRadioItem\n                        className={cn(item, 'pr-8 pl-2.5')}\n                        key={l.name}\n                        value={l.name}\n                      >\n                        {l.name}\n                        {l.hint && (\n                          <span className=\"text-muted-foreground text-xs\">\n                            {l.hint}\n                          </span>\n                        )}\n                      </DropdownMenuRadioItem>\n                    ))}\n                  </DropdownMenuRadioGroup>\n                  <DropdownMenuSeparator className=\"mx-1.5 my-1\" />\n                  <DropdownMenuSub>\n                    <DropdownMenuSubTrigger\n                      className={cn(\n                        item,\n                        '[&>svg]:size-3.5 [&>svg]:text-muted-foreground'\n                      )}\n                    >\n                      {model}\n                    </DropdownMenuSubTrigger>\n                    <DropdownMenuSubContent className=\"w-44 rounded-[14px] p-1.5\">\n                      <DropdownMenuRadioGroup\n                        className=\"flex flex-col gap-0.5\"\n                        onValueChange={setModel}\n                        value={model}\n                      >\n                        {models.map((m) => (\n                          <DropdownMenuRadioItem\n                            className={cn(item, 'pr-8 pl-2.5')}\n                            key={m}\n                            value={m}\n                          >\n                            {m}\n                          </DropdownMenuRadioItem>\n                        ))}\n                      </DropdownMenuRadioGroup>\n                    </DropdownMenuSubContent>\n                  </DropdownMenuSub>\n                </DropdownMenuGroup>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </InputGroupAddon>\n        </InputGroup>\n        <p className=\"text-muted-foreground text-xs\">\n          AI can make mistakes. Check important info.\n        </p>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "chat"
      ]
    },
    {
      "name": "chat-03",
      "type": "registry:block",
      "title": "Chat Application",
      "description": "A chat application block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "button",
        "collapsible",
        "command",
        "dropdown-menu",
        "kbd",
        "message-scroller",
        "sidebar",
        "textarea"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/chat/chat-03/components/command-menu.tsx",
          "type": "registry:component",
          "target": "components/chat-03/command-menu.tsx",
          "content": "'use client';\n\nimport { IconFileText, IconMessage, IconPlus } from '@tabler/icons-react';\nimport { useEffect, useState } from 'react';\nimport {\n  Command,\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandShortcut,\n} from '@/components/ui/command';\nimport { Kbd } from '@/components/ui/kbd';\nimport type { Thread } from '@/components/chat-03/conversation';\n\ntype Props = {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  threads: Thread[];\n  onOpen: (id: string) => void;\n  onNew: (draft: string) => void;\n};\n\nconst heading =\n  '**:[[cmdk-group-heading]]:px-2.5 **:[[cmdk-group-heading]]:font-semibold **:[[cmdk-group-heading]]:text-[10px] **:[[cmdk-group-heading]]:uppercase **:[[cmdk-group-heading]]:tracking-[0.08em] **:[[cmdk-group-heading]]:text-muted-foreground/80';\nconst row = 'h-10 gap-3 rounded-lg px-2.5 text-[13px]';\n\nfunction useCommandKey(onToggle: () => void) {\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault();\n        onToggle();\n      }\n    };\n    document.addEventListener('keydown', down);\n    return () => document.removeEventListener('keydown', down);\n  }, [onToggle]);\n}\n\nexport function CommandMenu({\n  open,\n  onOpenChange,\n  threads,\n  onOpen,\n  onNew,\n}: Props) {\n  const [query, setQuery] = useState('');\n  const q = query.trim().toLowerCase();\n  useCommandKey(() => onOpenChange(!open));\n\n  const messages = q\n    ? threads.flatMap((t) =>\n        t.turns\n          .filter(\n            (m) => m.role === 'assistant' && m.text.toLowerCase().includes(q)\n          )\n          .slice(0, 1)\n          .map((m) => {\n            const at = m.text.toLowerCase().indexOf(q);\n            const start = Math.max(0, at - 40);\n            return {\n              id: m.id,\n              threadId: t.id,\n              thread: t.title,\n              snippet: `${start > 0 ? '…' : ''}${m.text.slice(start, start + 90)}…`,\n            };\n          })\n      )\n    : [];\n\n  const pick = (fn: () => void) => {\n    fn();\n    onOpenChange(false);\n    setQuery('');\n  };\n\n  return (\n    <CommandDialog\n      className=\"max-w-140 rounded-[14px]!\"\n      description=\"Search chats and messages\"\n      onOpenChange={(v) => {\n        onOpenChange(v);\n        if (!v) {\n          setQuery('');\n        }\n      }}\n      open={open}\n      title=\"Search\"\n    >\n      <Command\n        className=\"p-0\"\n        filter={(value, search) =>\n          value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0\n        }\n      >\n        <CommandInput\n          onValueChange={setQuery}\n          placeholder=\"Search chats…\"\n          value={query}\n        />\n        <CommandList className=\"max-h-80 p-1\">\n          <CommandEmpty className=\"py-8 text-[13px] text-muted-foreground\">\n            No results for “{query}”\n          </CommandEmpty>\n          <CommandGroup className={heading} heading=\"Chats\">\n            {threads.map((t) => (\n              <CommandItem\n                className={row}\n                key={t.id}\n                onSelect={() => pick(() => onOpen(t.id))}\n                value={`${t.title} ${t.preview}`}\n              >\n                <IconMessage className=\"text-muted-foreground\" stroke={1.8} />\n                <span className=\"flex min-w-0 grow items-baseline gap-1.5\">\n                  <span className=\"shrink-0 font-medium\">{t.title}</span>\n                  <span className=\"truncate text-muted-foreground text-xs\">\n                    {t.preview}\n                  </span>\n                </span>\n                <CommandShortcut className=\"font-mono text-[11px] tracking-normal\">\n                  {t.updated}\n                </CommandShortcut>\n              </CommandItem>\n            ))}\n          </CommandGroup>\n          {messages.length > 0 && (\n            <CommandGroup className={heading} heading=\"Messages\">\n              {messages.map((m) => (\n                <CommandItem\n                  className={row}\n                  key={m.id}\n                  onSelect={() => pick(() => onOpen(m.threadId))}\n                  value={m.snippet}\n                >\n                  <IconFileText\n                    className=\"text-muted-foreground\"\n                    stroke={1.8}\n                  />\n                  <span className=\"grow truncate text-muted-foreground\">\n                    {m.snippet}\n                  </span>\n                  <CommandShortcut className=\"shrink-0 whitespace-nowrap text-[11px] tracking-normal\">\n                    {m.thread}\n                  </CommandShortcut>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n          )}\n          <CommandGroup className={heading} forceMount heading=\"Actions\">\n            <CommandItem\n              className={row}\n              forceMount\n              onSelect={() => pick(() => onNew(query.trim()))}\n              value={`new chat ${query}`}\n            >\n              <IconPlus className=\"text-muted-foreground\" stroke={2} />\n              <span className=\"grow\">\n                {q ? `New chat about “${query.trim()}”` : 'New chat'}\n              </span>\n              <CommandShortcut className=\"font-mono text-[11px] tracking-normal\">\n                ⌘N\n              </CommandShortcut>\n            </CommandItem>\n          </CommandGroup>\n        </CommandList>\n        <div className=\"flex h-9 items-center gap-3.5 border-t bg-muted/40 px-3.5 text-[11px] text-muted-foreground\">\n          <span className=\"flex items-center gap-1.25\">\n            <Kbd className=\"h-4.5 bg-background px-1.25 font-mono text-[10px] ring-1 ring-border\">\n              ↑↓\n            </Kbd>\n            Navigate\n          </span>\n          <span className=\"flex items-center gap-1.25\">\n            <Kbd className=\"h-4.5 bg-background px-1.25 font-mono text-[10px] ring-1 ring-border\">\n              ↵\n            </Kbd>\n            Open\n          </span>\n        </div>\n      </Command>\n    </CommandDialog>\n  );\n}\n"
        },
        {
          "path": "content/components/chat/chat-03/components/conversation.tsx",
          "type": "registry:component",
          "target": "components/chat-03/conversation.tsx",
          "content": "'use client';\n\nimport {\n  IconAlertCircle,\n  IconChevronDown,\n  IconChevronRight,\n  IconCopy,\n  IconPencil,\n  IconRefresh,\n  IconThumbDown,\n  IconThumbUp,\n} from '@tabler/icons-react';\nimport React, { Fragment } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from '@/components/ui/collapsible';\nimport {\n  MessageScroller,\n  MessageScrollerButton,\n  MessageScrollerContent,\n  MessageScrollerItem,\n  MessageScrollerProvider,\n  MessageScrollerViewport,\n} from '@/components/ui/message-scroller';\nimport { cn } from '@/lib/utils';\n\nexport type Turn = {\n  id: string;\n  role: 'user' | 'assistant';\n  text: string;\n  reasoning?: string;\n  thought?: number;\n  streaming?: boolean;\n  error?: boolean;\n  day?: Thread['group'];\n};\n\nexport type Thread = {\n  id: string;\n  title: string;\n  preview: string;\n  updated: string;\n  group: 'today' | 'yesterday' | 'week';\n  pinned?: boolean;\n  turns: Turn[];\n};\n\nconst easeOut = 'ease-[cubic-bezier(0.23,1,0.32,1)]';\nconst press = `${easeOut} transition-[scale,background-color] duration-150 active:scale-[0.96] motion-reduce:active:scale-100`;\nconst enter = `${easeOut} motion-reduce:slide-in-from-bottom-0 fade-in slide-in-from-bottom-1 animate-in duration-200`;\n\ntype Props = {\n  thread: Thread | null;\n  recent: Thread[];\n  altModel: string;\n  onOpen: (id: string) => void;\n  onEdit: (turn: Turn) => void;\n  onRetry: (turnId: string, model?: string) => void;\n};\n\nexport function Conversation({\n  thread,\n  recent,\n  altModel,\n  onOpen,\n  onEdit,\n  onRetry,\n}: Props) {\n  if (!thread) {\n    return (\n      <div className=\"flex grow items-center justify-center px-8\">\n        <div className={cn(enter, 'flex w-full max-w-160 flex-col gap-10')}>\n          <div className=\"flex flex-col gap-1.5\">\n            <h1 className=\"font-semibold text-[34px]/10 tracking-[-0.025em]\">\n              Where were we?\n            </h1>\n            <p className=\"text-[15px]/5.5 text-muted-foreground\">\n              Nothing here yet. Ask anything, or pick up a thread.\n            </p>\n          </div>\n          <div className=\"flex flex-col\">\n            <p className=\"pb-1 font-semibold text-[10px] text-muted-foreground/80 uppercase tracking-[0.08em]\">\n              Continue\n            </p>\n            {recent.map((t) => (\n              <button\n                className=\"-mx-2 flex h-13 items-center gap-3.5 rounded-lg not-last:border-muted not-last:border-b px-2 text-left transition-colors hover:bg-muted/60\"\n                key={t.id}\n                onClick={() => onOpen(t.id)}\n                type=\"button\"\n              >\n                <span className=\"flex grow flex-col gap-0.5\">\n                  <span className=\"font-medium text-sm/4.5\">{t.title}</span>\n                  <span className=\"text-muted-foreground text-xs\">\n                    {t.preview}\n                  </span>\n                </span>\n                <span className=\"font-mono text-[11px] text-muted-foreground/80\">\n                  {t.updated}\n                </span>\n              </button>\n            ))}\n          </div>\n        </div>\n      </div>\n    );\n  }\n\n  const action = (\n    label: string,\n    icon: React.ReactNode,\n    onClick?: () => void\n  ) => (\n    <Button\n      aria-label={label}\n      className={cn(press, 'size-6.5 rounded-lg text-muted-foreground')}\n      onClick={onClick}\n      size=\"icon-sm\"\n      variant=\"ghost\"\n    >\n      {icon}\n    </Button>\n  );\n\n  return (\n    <MessageScrollerProvider autoScroll>\n      <MessageScroller className=\"grow\">\n        <MessageScrollerViewport>\n          <MessageScrollerContent className=\"mx-auto w-full max-w-160 gap-4.5 px-4 pt-6 pb-4\">\n            {thread.turns.map((turn, i) => {\n              const day = turn.day ?? thread.group;\n              const prev = thread.turns[i - 1];\n              const divider = !prev || (prev.day ?? thread.group) !== day;\n              return (\n                <Fragment key={turn.id}>\n                  {divider && (\n                    <div className=\"flex items-center gap-3\">\n                      <div className=\"h-px grow bg-muted\" />\n                      <span className=\"font-medium text-[11px] text-muted-foreground/80\">\n                        {\n                          {\n                            today: 'Today',\n                            yesterday: 'Yesterday',\n                            week: 'This week',\n                          }[day]\n                        }\n                      </span>\n                      <div className=\"h-px grow bg-muted\" />\n                    </div>\n                  )}\n                  {turn.role === 'user' ? (\n                    <MessageScrollerItem\n                      className=\"group/turn flex flex-row-reverse items-center gap-2\"\n                      key={turn.id}\n                      messageId={turn.id}\n                    >\n                      <div className=\"max-w-105 rounded-[20px] bg-muted px-4 py-2.5 text-[15px]/5.5\">\n                        {turn.text}\n                      </div>\n                      <div className=\"flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-focus-within/turn:opacity-100 group-hover/turn:opacity-100\">\n                        {action('Edit', <IconPencil stroke={1.8} />, () =>\n                          onEdit(turn)\n                        )}\n                        {action('Copy', <IconCopy stroke={1.8} />, () =>\n                          navigator.clipboard.writeText(turn.text)\n                        )}\n                      </div>\n                    </MessageScrollerItem>\n                  ) : (\n                    <MessageScrollerItem\n                      className={cn(\n                        'flex flex-col gap-2.5',\n                        turn.streaming && enter\n                      )}\n                      key={turn.id}\n                      messageId={turn.id}\n                    >\n                      {turn.error ? (\n                        <>\n                          <div className=\"flex items-start gap-3 rounded-xl bg-destructive/5 px-4 py-3.5 ring-1 ring-destructive/15\">\n                            <IconAlertCircle\n                              className=\"mt-px size-4 shrink-0 text-destructive\"\n                              stroke={2}\n                            />\n                            <div className=\"flex flex-col gap-1\">\n                              <p className=\"font-semibold text-destructive text-sm/5\">\n                                The response was interrupted\n                              </p>\n                              <p className=\"text-[13px]/5 text-destructive/80\">\n                                The model stopped before finishing. Your message\n                                was not lost. Retry with the same model or\n                                switch to a different one.\n                              </p>\n                            </div>\n                          </div>\n                          <div className=\"flex items-center gap-2 pt-0.5\">\n                            <Button\n                              className={cn(press, 'text-[13px]')}\n                              onClick={() => onRetry(turn.id)}\n                            >\n                              <IconRefresh className=\"size-3.5\" stroke={2} />\n                              Retry\n                            </Button>\n                            <Button\n                              className={cn(press, 'text-[13px]')}\n                              onClick={() => onRetry(turn.id, altModel)}\n                              variant=\"outline\"\n                            >\n                              Retry with {altModel}\n                            </Button>\n                          </div>\n                        </>\n                      ) : (\n                        <>\n                          <Collapsible className=\"flex flex-col gap-2\">\n                            <CollapsibleTrigger\n                              className={cn(\n                                'group/thought flex w-fit items-center gap-2 rounded-md font-medium text-[13px] text-muted-foreground outline-none focus-visible:ring-3 focus-visible:ring-ring/50',\n                                turn.streaming &&\n                                  !turn.text &&\n                                  'shimmer text-primary'\n                              )}\n                            >\n                              <IconChevronRight\n                                className=\"size-3.5 transition-transform group-data-[panel-open]/thought:rotate-90\"\n                                stroke={2}\n                              />\n                              {turn.thought\n                                ? `Thought for ${turn.thought}s`\n                                : 'Thinking…'}\n                            </CollapsibleTrigger>\n                            <CollapsibleContent className=\"border-muted border-l-2 pl-3.5 text-[13px]/5 text-muted-foreground\">\n                              {turn.reasoning}\n                            </CollapsibleContent>\n                          </Collapsible>\n                          {turn.text && (\n                            <p className=\"text-[15px]/6\">{turn.text}</p>\n                          )}\n                          {!turn.streaming && (\n                            <div className=\"-ml-1.5 flex items-center gap-0.5\">\n                              {action('Copy', <IconCopy stroke={1.8} />, () =>\n                                navigator.clipboard.writeText(turn.text)\n                              )}\n                              {action(\n                                'Good response',\n                                <IconThumbUp stroke={1.8} />\n                              )}\n                              {action(\n                                'Bad response',\n                                <IconThumbDown stroke={1.8} />\n                              )}\n                              {action(\n                                'Regenerate',\n                                <IconRefresh stroke={1.8} />,\n                                () => onRetry(turn.id)\n                              )}\n                            </div>\n                          )}\n                        </>\n                      )}\n                    </MessageScrollerItem>\n                  )}\n                </Fragment>\n              );\n            })}\n          </MessageScrollerContent>\n        </MessageScrollerViewport>\n        <MessageScrollerButton\n          className={cn(\n            easeOut,\n            'bottom-2 h-8 gap-1.5 rounded-full border-border bg-background px-3.5 font-medium text-[13px] shadow-xs transition-[translate,scale,opacity,background-color]'\n          )}\n          size=\"sm\"\n          variant=\"outline\"\n        >\n          <IconChevronDown className=\"size-3.5\" stroke={2} />\n          Jump to latest\n        </MessageScrollerButton>\n      </MessageScroller>\n    </MessageScrollerProvider>\n  );\n}\n"
        },
        {
          "path": "content/components/chat/chat-03/components/composer.tsx",
          "type": "registry:component",
          "target": "components/chat-03/composer.tsx",
          "content": "'use client';\n\nimport {\n  IconArrowUp,\n  IconMicrophone,\n  IconPaperclip,\n  IconPhoto,\n  IconPlayerStopFilled,\n  IconPlus,\n  IconWorld,\n  IconX,\n} from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Textarea } from '@/components/ui/textarea';\nimport { cn } from '@/lib/utils';\n\nexport type Attachment = { name: string; meta: string; kind: string };\n\nconst easeOut = 'ease-[cubic-bezier(0.23,1,0.32,1)]';\nconst press = `${easeOut} transition-[scale,background-color] duration-150 active:scale-[0.96] motion-reduce:active:scale-100`;\nconst swap = `${easeOut} fade-in zoom-in-95 motion-reduce:zoom-in-100 animate-in duration-150`;\nconst round = 'size-7.5 rounded-full';\n\ntype Props = {\n  draft: string;\n  onDraft: (v: string) => void;\n  attachment: Attachment | null;\n  onAttachment: (a: Attachment | null) => void;\n  streaming: boolean;\n  onSend: () => void;\n  onStop: () => void;\n};\n\nexport function Composer({\n  draft,\n  onDraft,\n  attachment,\n  onAttachment,\n  streaming,\n  onSend,\n  onStop,\n}: Props) {\n  const [search, setSearch] = useState(false);\n  const [listening, setListening] = useState(false);\n  const canSend = draft.trim().length > 0 && !streaming;\n\n  return (\n    <form\n      className=\"mx-auto flex w-full max-w-160 flex-col px-4 pt-3 pb-5\"\n      onSubmit={(e) => {\n        e.preventDefault();\n        if (canSend) {\n          onSend();\n        }\n      }}\n    >\n      <div\n        className={cn(\n          easeOut,\n          'flex flex-col gap-2.5 rounded-3xl border border-border bg-background p-3 shadow-[0_1px_2px_oklch(0_0_0/0.04),0_8px_24px_oklch(0_0_0/0.05)] transition-[box-shadow,border-color] duration-150 focus-within:border-primary focus-within:ring-4 focus-within:ring-primary/12'\n        )}\n      >\n        {attachment && (\n          <div className={cn(swap, 'flex')}>\n            <div className=\"flex h-12 items-center gap-2.5 rounded-xl border border-border bg-muted/50 pr-3 pl-2\">\n              <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-destructive/10 font-mono font-semibold text-[9px] text-destructive tracking-[0.04em]\">\n                {attachment.kind}\n              </span>\n              <span className=\"flex flex-col gap-1\">\n                <span className=\"font-medium text-[13px]/4\">\n                  {attachment.name}\n                </span>\n                <span className=\"text-[11px]/3.5 text-muted-foreground\">\n                  {attachment.meta}\n                </span>\n              </span>\n              <button\n                aria-label=\"Remove attachment\"\n                className=\"rounded-md p-0.5 text-muted-foreground transition-colors hover:text-foreground\"\n                onClick={() => onAttachment(null)}\n                type=\"button\"\n              >\n                <IconX className=\"size-3.5\" stroke={2} />\n              </button>\n            </div>\n          </div>\n        )}\n        <Textarea\n          aria-label=\"Message\"\n          className=\"field-sizing-content max-h-48 min-h-0 resize-none rounded-none border-0 bg-transparent px-1.5 py-0.5 text-sm/5 shadow-none focus-visible:ring-0 md:text-sm/5 dark:bg-transparent\"\n          onChange={(e) => onDraft(e.target.value)}\n          onKeyDown={(e) => {\n            if (e.key === 'Enter' && !e.shiftKey) {\n              e.preventDefault();\n              e.currentTarget.form?.requestSubmit();\n            }\n          }}\n          placeholder={listening ? 'Listening…' : 'Ask anything'}\n          rows={1}\n          value={draft}\n        />\n        <div className=\"flex items-center gap-2\">\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              render={\n                <Button\n                  aria-label=\"Add attachment\"\n                  className={cn(press, round)}\n                  size=\"icon-sm\"\n                  variant=\"outline\"\n                />\n              }\n            >\n              <IconPlus stroke={2} />\n            </DropdownMenuTrigger>\n            <DropdownMenuContent\n              align=\"start\"\n              className=\"w-52 rounded-xl p-1.5\"\n              side=\"top\"\n              sideOffset={10}\n            >\n              <DropdownMenuGroup className=\"flex flex-col gap-0.5\">\n                <DropdownMenuItem\n                  className=\"h-8 gap-2.5 rounded-md px-2.5 text-[13px]\"\n                  onClick={() =>\n                    onAttachment({\n                      name: 'installer-quote.pdf',\n                      meta: '2 pages · 412 KB',\n                      kind: 'PDF',\n                    })\n                  }\n                >\n                  <IconPaperclip stroke={1.8} />\n                  Add files\n                </DropdownMenuItem>\n                <DropdownMenuItem\n                  className=\"h-8 gap-2.5 rounded-md px-2.5 text-[13px]\"\n                  onClick={() =>\n                    onAttachment({\n                      name: 'boiler-room.jpg',\n                      meta: '3024 × 4032 · 2.1 MB',\n                      kind: 'JPG',\n                    })\n                  }\n                >\n                  <IconPhoto stroke={1.8} />\n                  Add photos\n                </DropdownMenuItem>\n              </DropdownMenuGroup>\n            </DropdownMenuContent>\n          </DropdownMenu>\n          <Button\n            aria-pressed={search}\n            className={cn(\n              press,\n              'h-7.5 rounded-full px-2.75 text-xs',\n              search &&\n                'border-primary/30 bg-primary/8 text-primary hover:bg-primary/12'\n            )}\n            onClick={() => setSearch(!search)}\n            size=\"sm\"\n            variant=\"outline\"\n          >\n            <IconWorld stroke={2} />\n            Search\n          </Button>\n          <div className=\"grow\" />\n          <Button\n            aria-label={listening ? 'Stop listening' : 'Voice input'}\n            aria-pressed={listening}\n            className={cn(\n              press,\n              round,\n              listening &&\n                'bg-destructive/15 text-destructive hover:bg-destructive/25'\n            )}\n            onClick={() => setListening(!listening)}\n            size=\"icon-sm\"\n            variant=\"ghost\"\n          >\n            {listening ? (\n              <IconPlayerStopFilled className=\"animate-pulse motion-reduce:animate-none\" />\n            ) : (\n              <IconMicrophone stroke={2} />\n            )}\n          </Button>\n          {streaming ? (\n            <Button\n              aria-label=\"Stop generating\"\n              className={cn(swap, press, round)}\n              onClick={onStop}\n              size=\"icon-sm\"\n            >\n              <IconPlayerStopFilled className=\"size-3\" />\n            </Button>\n          ) : (\n            <Button\n              aria-label=\"Send\"\n              className={cn(\n                swap,\n                press,\n                round,\n                !canSend &&\n                  'bg-muted text-muted-foreground/70 hover:bg-muted disabled:opacity-100'\n              )}\n              disabled={!canSend}\n              size=\"icon-sm\"\n              type=\"submit\"\n            >\n              <IconArrowUp stroke={2.2} />\n            </Button>\n          )}\n        </div>\n      </div>\n    </form>\n  );\n}\n"
        },
        {
          "path": "content/components/chat/chat-03/components/chat-sidebar.tsx",
          "type": "registry:component",
          "target": "components/chat-03/chat-sidebar.tsx",
          "content": "'use client';\n\nimport {\n  IconArchive,\n  IconDots,\n  IconHelpCircle,\n  IconKeyboard,\n  IconLogout,\n  IconPencil,\n  IconPin,\n  IconPlus,\n  IconSearch,\n  IconSelector,\n  IconSettings,\n  IconShare,\n  IconSunMoon,\n  IconTrash,\n} from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Kbd, KbdGroup } from '@/components/ui/kbd';\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupContent,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarMenu,\n  SidebarMenuAction,\n  SidebarMenuBadge,\n  SidebarMenuButton,\n  SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport type { Thread } from '@/components/chat-03/conversation';\n\nconst groups: { key: Thread['group'] | 'pinned'; label: string }[] = [\n  { key: 'pinned', label: 'Pinned' },\n  { key: 'today', label: 'Today' },\n  { key: 'yesterday', label: 'Yesterday' },\n  { key: 'week', label: 'Previous 7 days' },\n];\n\nconst item = 'h-8 gap-2.5 rounded-md px-2.5 text-[13px]';\n\ntype Props = {\n  user: {\n    name: string;\n    email: string;\n    plan: string;\n    renews: string;\n    avatar: string;\n  };\n  threads: Thread[];\n  activeId: string | null;\n  onSelect: (id: string) => void;\n  onNew: () => void;\n  onSearch: () => void;\n  onChange: (threads: Thread[]) => void;\n};\n\nexport function ChatSidebar({\n  user,\n  threads,\n  activeId,\n  onSelect,\n  onNew,\n  onSearch,\n  onChange,\n}: Props) {\n  const [renameId, setRenameId] = useState<string | null>(null);\n  const [name, setName] = useState('');\n\n  const commitRename = () => {\n    if (renameId && name.trim()) {\n      onChange(\n        threads.map((t) =>\n          t.id === renameId ? { ...t, title: name.trim() } : t\n        )\n      );\n    }\n    setRenameId(null);\n  };\n\n  return (\n    <Sidebar className=\"border-sidebar-border\" collapsible=\"offcanvas\">\n      <SidebarHeader className=\"gap-2 px-3 pt-3 pb-1\">\n        <Button className=\"h-8.5 rounded-lg text-[13px]\" onClick={onNew}>\n          <IconPlus className=\"size-3.5\" stroke={2.2} />\n          New chat\n        </Button>\n        <button\n          className=\"flex h-8 items-center gap-1.75 rounded-lg border border-border pr-1.5 pl-2.5 text-[13px] text-muted-foreground transition-colors hover:bg-muted/60 focus-visible:border-primary focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary/20\"\n          onClick={onSearch}\n          type=\"button\"\n        >\n          <IconSearch className=\"size-3.5\" stroke={2} />\n          <span className=\"grow text-left\">Search chats</span>\n          <Kbd className=\"h-4.5 border border-border bg-sidebar px-1.25 text-[10px]\">\n            ⌘K\n          </Kbd>\n        </button>\n      </SidebarHeader>\n\n      <SidebarContent className=\"gap-0 px-3 pt-2\">\n        {groups.map(({ key, label }) => {\n          const list = threads.filter((t) =>\n            key === 'pinned' ? t.pinned : !t.pinned && t.group === key\n          );\n          if (list.length === 0) {\n            return null;\n          }\n          return (\n            <SidebarGroup className=\"p-0 pb-2\" key={key}>\n              <SidebarGroupLabel className=\"h-5 px-2 font-semibold text-[10px] text-muted-foreground/80 uppercase tracking-[0.08em]\">\n                {label}\n              </SidebarGroupLabel>\n              <SidebarGroupContent>\n                <SidebarMenu className=\"gap-0.5\">\n                  {list.map((t) => (\n                    <SidebarMenuItem key={t.id}>\n                      {renameId === t.id ? (\n                        <input\n                          aria-label=\"Thread name\"\n                          autoFocus\n                          className=\"h-7 w-full rounded-md border border-primary bg-background px-2 text-[13px] outline-none ring-3 ring-primary/20\"\n                          onBlur={commitRename}\n                          onChange={(e) => setName(e.target.value)}\n                          onFocus={(e) => e.target.select()}\n                          onKeyDown={(e) => {\n                            if (e.key === 'Enter') {\n                              commitRename();\n                            }\n                            if (e.key === 'Escape') {\n                              setRenameId(null);\n                            }\n                          }}\n                          value={name}\n                        />\n                      ) : (\n                        <>\n                          <SidebarMenuButton\n                            className=\"h-7 px-2 text-[13px] data-active:bg-muted\"\n                            isActive={t.id === activeId}\n                            onClick={() => onSelect(t.id)}\n                          >\n                            <span>{t.title}</span>\n                          </SidebarMenuButton>\n                          <SidebarMenuBadge\n                            className={cn(\n                              'top-1! right-1.5 font-mono font-normal text-[11px] text-muted-foreground/80 group-focus-within/menu-item:opacity-0 group-hover/menu-item:opacity-0 group-has-aria-expanded/menu-item:opacity-0',\n                              t.pinned && 'right-1'\n                            )}\n                          >\n                            {t.pinned ? (\n                              <IconPin className=\"size-3\" stroke={1.8} />\n                            ) : (\n                              t.updated\n                            )}\n                          </SidebarMenuBadge>\n                          <DropdownMenu>\n                            <DropdownMenuTrigger\n                              render={\n                                <SidebarMenuAction\n                                  aria-label=\"Thread actions\"\n                                  className=\"top-1! text-muted-foreground\"\n                                  showOnHover\n                                />\n                              }\n                            >\n                              <IconDots className=\"size-3.5\" />\n                            </DropdownMenuTrigger>\n                            <DropdownMenuContent\n                              align=\"start\"\n                              className=\"w-48 rounded-xl p-1.5\"\n                              finalFocus={false}\n                              side=\"bottom\"\n                              sideOffset={6}\n                            >\n                              <DropdownMenuGroup className=\"flex flex-col gap-0.5\">\n                                <DropdownMenuItem\n                                  className={item}\n                                  onClick={() =>\n                                    onChange(\n                                      threads.map((x) =>\n                                        x.id === t.id\n                                          ? { ...x, pinned: !x.pinned }\n                                          : x\n                                      )\n                                    )\n                                  }\n                                >\n                                  <IconPin stroke={1.8} />\n                                  {t.pinned ? 'Unpin' : 'Pin'}\n                                </DropdownMenuItem>\n                                <DropdownMenuItem\n                                  className={item}\n                                  onClick={() => {\n                                    setRenameId(t.id);\n                                    setName(t.title);\n                                  }}\n                                >\n                                  <IconPencil stroke={1.8} />\n                                  Rename\n                                </DropdownMenuItem>\n                                <DropdownMenuItem className={item}>\n                                  <IconShare stroke={1.8} />\n                                  Share\n                                </DropdownMenuItem>\n                                <DropdownMenuItem\n                                  className={item}\n                                  onClick={() =>\n                                    onChange(\n                                      threads.filter((x) => x.id !== t.id)\n                                    )\n                                  }\n                                >\n                                  <IconArchive stroke={1.8} />\n                                  Archive\n                                </DropdownMenuItem>\n                              </DropdownMenuGroup>\n                              <DropdownMenuSeparator className=\"my-1\" />\n                              <DropdownMenuItem\n                                className={item}\n                                onClick={() =>\n                                  onChange(threads.filter((x) => x.id !== t.id))\n                                }\n                                variant=\"destructive\"\n                              >\n                                <IconTrash stroke={1.8} />\n                                Delete\n                              </DropdownMenuItem>\n                            </DropdownMenuContent>\n                          </DropdownMenu>\n                        </>\n                      )}\n                    </SidebarMenuItem>\n                  ))}\n                </SidebarMenu>\n              </SidebarGroupContent>\n            </SidebarGroup>\n          );\n        })}\n      </SidebarContent>\n\n      <SidebarFooter className=\"border-sidebar-border border-t p-0\">\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <button\n                className=\"flex h-14 w-full items-center gap-2.5 px-3 text-left outline-none transition-colors hover:bg-muted/60 focus-visible:bg-muted/60 aria-expanded:bg-muted/60\"\n                type=\"button\"\n              />\n            }\n          >\n            <Avatar className=\"size-7.5 rounded-lg\">\n              <AvatarImage\n                alt={user.name}\n                className=\"rounded-lg\"\n                src={user.avatar}\n              />\n              <AvatarFallback className=\"rounded-lg\">\n                {user.name[0]}\n              </AvatarFallback>\n            </Avatar>\n            <span className=\"flex min-w-0 grow flex-col gap-px\">\n              <span className=\"truncate font-medium text-[13px]\">\n                {user.name}\n              </span>\n              <span className=\"truncate text-[11px] text-muted-foreground\">\n                {user.plan} · {user.email}\n              </span>\n            </span>\n            <IconSelector\n              className=\"size-3.5 shrink-0 text-muted-foreground/80\"\n              stroke={2}\n            />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"center\"\n            className=\"w-[calc(var(--anchor-width)-1rem)] rounded-xl p-1.5\"\n            side=\"top\"\n            sideOffset={8}\n          >\n            <div className=\"flex items-center gap-2.5 px-2 pt-1.5 pb-2.5\">\n              <Avatar className=\"size-8 rounded-lg\">\n                <AvatarImage\n                  alt={user.name}\n                  className=\"rounded-lg\"\n                  src={user.avatar}\n                />\n                <AvatarFallback className=\"rounded-lg\">\n                  {user.name[0]}\n                </AvatarFallback>\n              </Avatar>\n              <div className=\"flex min-w-0 flex-col gap-px\">\n                <span className=\"font-medium text-[13px]\">{user.name}</span>\n                <span className=\"text-[11px] text-muted-foreground\">\n                  {user.email}\n                </span>\n              </div>\n            </div>\n            <DropdownMenuSeparator />\n            <DropdownMenuGroup className=\"flex flex-col gap-0.5 py-0.5\">\n              <DropdownMenuItem className={item}>\n                <IconSettings stroke={1.8} />\n                Settings\n                <KbdGroup className=\"ml-auto\">\n                  <Kbd>⌘</Kbd>\n                  <Kbd>,</Kbd>\n                </KbdGroup>\n              </DropdownMenuItem>\n              <DropdownMenuItem className={item}>\n                <IconSunMoon stroke={1.8} />\n                Appearance\n                <DropdownMenuShortcut className=\"text-xs tracking-normal\">\n                  System\n                </DropdownMenuShortcut>\n              </DropdownMenuItem>\n              <DropdownMenuItem className={item}>\n                <IconKeyboard stroke={1.8} />\n                Keyboard shortcuts\n                <KbdGroup className=\"ml-auto\">\n                  <Kbd>⌘</Kbd>\n                  <Kbd>/</Kbd>\n                </KbdGroup>\n              </DropdownMenuItem>\n              <DropdownMenuItem className={item}>\n                <IconHelpCircle stroke={1.8} />\n                Help & feedback\n              </DropdownMenuItem>\n            </DropdownMenuGroup>\n            <DropdownMenuSeparator />\n            <div className=\"my-0.5 flex items-center justify-between rounded-lg bg-muted/60 px-2.5 py-2\">\n              <div className=\"flex flex-col gap-px\">\n                <span className=\"font-medium text-xs\">{user.plan} plan</span>\n                <span className=\"text-[11px] text-muted-foreground\">\n                  Renews {user.renews}\n                </span>\n              </div>\n              <Button className=\"h-auto p-0 text-xs\" size=\"sm\" variant=\"link\">\n                Manage\n              </Button>\n            </div>\n            <DropdownMenuSeparator />\n            <DropdownMenuItem className={item}>\n              <IconLogout stroke={1.8} />\n              Log out\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </SidebarFooter>\n    </Sidebar>\n  );\n}\n"
        },
        {
          "path": "content/components/chat/chat-03/components/logos.tsx",
          "type": "registry:component",
          "target": "components/chat-03/logos.tsx",
          "content": "import type { ComponentProps } from 'react';\n\nexport function OpenAILogo(props: ComponentProps<'svg'>) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      fill=\"currentColor\"\n      viewBox=\"0 0 24 24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}\n    >\n      <path d=\"M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.795.795 0 0 0-.393.681zm1.097-2.365 2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5z\" />\n    </svg>\n  );\n}\n\nexport function ClaudeLogo(props: ComponentProps<'svg'>) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      fill=\"currentColor\"\n      viewBox=\"0 0 24 24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}\n    >\n      <path d=\"m4.71 15.96 4.71-2.64.08-.23-.08-.13h-.23l-.79-.05-2.69-.07-2.33-.1-2.26-.12-.57-.12L0 11.8l.06-.38.48-.32.68.06 1.51.1 2.27.16 1.65.1 2.44.25h.39l.05-.16-.13-.1-.1-.1-2.35-1.59-2.54-1.68-1.33-.97-.72-.49-.36-.46-.16-1.01.66-.72.88.06.23.06.89.69 1.91 1.48 2.49 1.83.37.3.15-.1.02-.07-.17-.27-1.36-2.46-1.45-2.5-.65-1.04-.17-.62a2.97 2.97 0 0 1-.1-.73L5.05.76l.42-.14.99.14.42.36.62 1.41 1 2.22 1.55 3.02.45.9.24.83.09.25h.16v-.15l.13-1.7.23-2.1.23-2.7.08-.76.37-.9.75-.5.58.28.48.69-.07.44-.29 1.85-.56 2.9-.36 1.94h.21l.25-.24 1-1.33 1.68-2.1.74-.84.87-.92.56-.44h1.05l.78 1.16-.35 1.19-1.09 1.38-.9 1.17-1.3 1.74-.8 1.4.07.11.19-.02 2.94-.63 1.59-.29 1.9-.32.85.4.1.4-.34.83-2.03.5-2.38.48-3.54.84-.05.03.05.06 1.6.15.68.04h1.67l3.12.23.81.54.49.66-.08.5-1.25.64-1.7-.4-3.95-.94-1.35-.34h-.19v.11l1.13 1.1 2.07 1.87 2.59 2.4.13.6-.33.47-.35-.05-2.28-1.72-.88-.77-1.99-1.68h-.13v.18l.46.67 2.42 3.64.13 1.12-.18.36-.63.22-.69-.13-1.42-1.99-1.47-2.25-1.18-2.01-.14.08-.7 7.51-.33.38-.75.29-.63-.48-.34-.78.34-1.53.4-2 .33-1.6.3-1.97.18-.66-.01-.04-.15.02-1.5 2.06-2.28 3.09-1.81 1.93-.43.17-.75-.39.07-.69.42-.62 2.49-3.17 1.5-1.97.97-1.14-.01-.16h-.06L2.4 18.5l-1.17.15-.5-.47.06-.77.24-.25 1.97-1.36z\" />\n    </svg>\n  );\n}\n"
        },
        {
          "path": "content/components/chat/chat-03/app/chat/data.json",
          "type": "registry:page",
          "target": "app/chat/data.json",
          "content": "{\n  \"user\": {\n    \"name\": \"Ephraim Duncan\",\n    \"email\": \"ephraim@duncan.land\",\n    \"plan\": \"Pro\",\n    \"renews\": \"Oct 12\",\n    \"avatar\": \"/avatar-01.png\"\n  },\n  \"models\": [\n    {\n      \"vendor\": \"openai\",\n      \"name\": \"GPT-6 Astra\",\n      \"hint\": \"Most capable for complex reasoning and coding\",\n      \"context\": \"1M\"\n    },\n    {\n      \"vendor\": \"openai\",\n      \"name\": \"GPT-5.6 Sol\",\n      \"hint\": \"Flagship for complex professional work\",\n      \"context\": \"1M\"\n    },\n    {\n      \"vendor\": \"openai\",\n      \"name\": \"GPT-5.6 Terra\",\n      \"hint\": \"Balances intelligence and cost\",\n      \"context\": \"1M\"\n    },\n    {\n      \"vendor\": \"openai\",\n      \"name\": \"GPT-5.6 Luna\",\n      \"hint\": \"Fastest and most affordable\",\n      \"context\": \"128K\"\n    },\n    {\n      \"vendor\": \"anthropic\",\n      \"name\": \"Claude Fable 5.1\",\n      \"hint\": \"Demanding reasoning and long-horizon agentic work\",\n      \"context\": \"1M\"\n    },\n    {\n      \"vendor\": \"anthropic\",\n      \"name\": \"Claude Opus 5\",\n      \"hint\": \"Complex agentic coding and enterprise work\",\n      \"context\": \"1M\"\n    },\n    {\n      \"vendor\": \"anthropic\",\n      \"name\": \"Claude Sonnet 5\",\n      \"hint\": \"Best combination of speed and intelligence\",\n      \"context\": \"1M\"\n    },\n    {\n      \"vendor\": \"anthropic\",\n      \"name\": \"Claude Haiku 4.5\",\n      \"hint\": \"Fastest with near-frontier intelligence\",\n      \"context\": \"200K\"\n    }\n  ],\n  \"replies\": [\n    {\n      \"reasoning\": \"The user wants a price check. Compare the quote against regional install averages, then weigh it against the payback window from earlier in the thread.\",\n      \"text\": \"Your quote lands near the middle of the range for a cold-climate unit of that size. The equipment line is fair; the labor line is about 10% above typical. Ask the installer to itemize ductwork changes, since that is where the extra usually hides. With the rebate applied, payback still comes in under 8 years.\"\n    },\n    {\n      \"reasoning\": \"Short factual answer. Note the trade-off between efficiency and noise so the user can decide.\",\n      \"text\": \"Most modern units run between 40 and 60 dB at the outdoor fan, roughly the level of a quiet conversation. Placing the unit away from bedroom windows and on a vibration pad makes a bigger difference than the model choice.\"\n    },\n    {\n      \"reasoning\": \"The user is asking about maintenance. List the few things that matter and skip the marketing extras.\",\n      \"text\": \"Very little. Clean or swap the filter every one to three months, keep leaves and snow off the outdoor unit, and book a professional check once a year to confirm refrigerant levels. That is the whole list.\"\n    }\n  ],\n  \"threads\": [\n    {\n      \"id\": \"design-tokens\",\n      \"title\": \"Design system tokens\",\n      \"preview\": \"Naming scheme for spacing and color\",\n      \"updated\": \"3d\",\n      \"group\": \"week\",\n      \"pinned\": true,\n      \"turns\": [\n        {\n          \"id\": \"dt-1\",\n          \"role\": \"user\",\n          \"text\": \"Should spacing tokens be named by size or by role?\"\n        },\n        {\n          \"id\": \"dt-2\",\n          \"role\": \"assistant\",\n          \"thought\": 4,\n          \"reasoning\": \"Both approaches are common. Recommend a two-layer scheme so the team gets stable role names without losing raw sizes.\",\n          \"text\": \"Use both, in two layers. Keep a raw scale (space-1 through space-12) and put role tokens on top of it (space-inline, space-stack, space-section). Components reference roles, so a scale change never touches component code.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"auth-refactor\",\n      \"title\": \"Auth refactor plan\",\n      \"preview\": \"Session cookies to short-lived JWTs\",\n      \"updated\": \"5d\",\n      \"group\": \"week\",\n      \"pinned\": true,\n      \"turns\": [\n        {\n          \"id\": \"ar-1\",\n          \"role\": \"user\",\n          \"text\": \"What is the safest order to migrate sessions to short-lived tokens?\"\n        },\n        {\n          \"id\": \"ar-2\",\n          \"role\": \"assistant\",\n          \"thought\": 6,\n          \"reasoning\": \"Migration needs a dual-read period. Outline steps that let the old and new paths coexist before cutting over.\",\n          \"text\": \"Issue both for a release, read both, then stop issuing the old one. Start by adding token issuance next to the existing session write, make every auth check accept either, and only after a full expiry window remove the session path.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"heat-pump\",\n      \"title\": \"Heat pump research\",\n      \"preview\": \"Payback math on replacing a gas boiler\",\n      \"updated\": \"2h\",\n      \"group\": \"today\",\n      \"turns\": [\n        {\n          \"id\": \"hp-1\",\n          \"role\": \"user\",\n          \"text\": \"How do heat pumps keep working when it's freezing outside?\"\n        },\n        {\n          \"id\": \"hp-2\",\n          \"role\": \"assistant\",\n          \"thought\": 8,\n          \"reasoning\": \"Explain the physics briefly: refrigerant boiling point is far below freezing, so there is still heat to extract. Give one concrete efficiency figure at a cold temperature.\",\n          \"text\": \"Even freezing air holds heat. A heat pump's refrigerant boils far below 0 °C, so it can absorb outdoor heat, compress it to raise the temperature, and release it inside. Modern cold-climate units keep 250–300% efficiency at −15 °C.\"\n        },\n        {\n          \"id\": \"hp-3\",\n          \"role\": \"user\",\n          \"text\": \"Is it worth replacing a gas boiler mid-life?\"\n        },\n        {\n          \"id\": \"hp-4\",\n          \"role\": \"assistant\",\n          \"thought\": 12,\n          \"reasoning\": \"This depends on the price ratio between electricity and gas, the remaining boiler life, and rebates. Give the rule of thumb and the three inputs.\",\n          \"text\": \"Usually yes, if your electricity-to-gas price ratio is under about 3.5. The math hinges on three numbers: your boiler's remaining life, local energy prices, and available rebates. Start with the rebates — in many regions they cover 30–40% of the install, which shortens payback to 6–8 years.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"sql-churn\",\n      \"title\": \"SQL for churn cohort\",\n      \"preview\": \"Window function draft, not yet run\",\n      \"updated\": \"5h\",\n      \"group\": \"today\",\n      \"turns\": [\n        {\n          \"id\": \"sc-1\",\n          \"role\": \"user\",\n          \"text\": \"Write a query that groups users by signup month and shows how many are still active 90 days later.\"\n        },\n        {\n          \"id\": \"sc-2\",\n          \"role\": \"assistant\",\n          \"thought\": 9,\n          \"reasoning\": \"Cohort by signup month, then join activity at day 90. Keep it to one CTE so it is easy to read.\",\n          \"text\": \"Group by date_trunc of the signup date, then left join an activity subquery filtered to events within 90 days of signup. Count distinct users in both, and divide for the retention rate. I drafted it with a single CTE so you can swap the window without touching the join.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"lisbon\",\n      \"title\": \"Trip to Lisbon in October\",\n      \"preview\": \"Four days, mostly on foot\",\n      \"updated\": \"2h\",\n      \"group\": \"today\",\n      \"turns\": [\n        {\n          \"id\": \"li-1\",\n          \"role\": \"user\",\n          \"text\": \"Is October a good month for Lisbon?\"\n        },\n        {\n          \"id\": \"li-2\",\n          \"role\": \"assistant\",\n          \"thought\": 3,\n          \"reasoning\": \"Weather and crowds are the two things the user cares about. Keep it short.\",\n          \"text\": \"One of the best. Days sit around 22 °C, the summer crowds are gone, and the sea is still warm enough to swim. Pack a light layer for evenings and expect a few short showers late in the month.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"q3-report\",\n      \"title\": \"Q3 report outline\",\n      \"preview\": \"Three sections drafted, intro missing\",\n      \"updated\": \"1d\",\n      \"group\": \"yesterday\",\n      \"turns\": [\n        {\n          \"id\": \"q3-1\",\n          \"role\": \"user\",\n          \"text\": \"Give me a three-part outline for a quarterly report to the board.\"\n        },\n        {\n          \"id\": \"q3-2\",\n          \"role\": \"assistant\",\n          \"thought\": 5,\n          \"reasoning\": \"Boards want results, then risks, then asks. Map the three sections to that order.\",\n          \"text\": \"Results first, then risks, then asks. Open with the three numbers that moved and why, follow with the two things that could go wrong next quarter, and close with the decisions you need from the board. Keep each section to one page.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"dockerfile\",\n      \"title\": \"Dockerfile multi-stage build\",\n      \"preview\": \"Trim the final image below 100 MB\",\n      \"updated\": \"1d\",\n      \"group\": \"yesterday\",\n      \"turns\": [\n        {\n          \"id\": \"df-1\",\n          \"role\": \"user\",\n          \"text\": \"Why is my Node image 1.2 GB?\"\n        },\n        {\n          \"id\": \"df-2\",\n          \"role\": \"assistant\",\n          \"thought\": 4,\n          \"reasoning\": \"Most likely a single-stage build carrying dev dependencies and the full base image. Recommend a multi-stage build on a slim base.\",\n          \"text\": \"You are shipping the build toolchain. Build in one stage on the full image, then copy only the output and production node_modules into a second stage based on node:slim or distroless. That alone usually lands under 150 MB.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"naming\",\n      \"title\": \"Naming a side project\",\n      \"preview\": \"Short, spellable, domain available\",\n      \"updated\": \"1d\",\n      \"group\": \"yesterday\",\n      \"turns\": [\n        {\n          \"id\": \"nm-1\",\n          \"role\": \"user\",\n          \"text\": \"What makes a product name easy to remember?\"\n        },\n        {\n          \"id\": \"nm-2\",\n          \"role\": \"assistant\",\n          \"thought\": 3,\n          \"reasoning\": \"Keep to the practical rules: short, easy to spell after hearing it once, and not already crowded.\",\n          \"text\": \"Two syllables, spellable after hearing it once, and no existing product in the same space with a similar sound. Say the name out loud in a sentence and see if you have to explain it.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"postgres\",\n      \"title\": \"Postgres index bloat\",\n      \"preview\": \"When to REINDEX versus VACUUM\",\n      \"updated\": \"3d\",\n      \"group\": \"week\",\n      \"turns\": [\n        {\n          \"id\": \"pg-1\",\n          \"role\": \"user\",\n          \"text\": \"How do I tell if an index is bloated?\"\n        },\n        {\n          \"id\": \"pg-2\",\n          \"role\": \"assistant\",\n          \"thought\": 6,\n          \"reasoning\": \"Point to pgstattuple for a real measurement, and give the rough threshold where REINDEX pays off.\",\n          \"text\": \"Install pgstattuple and check the leaf fragmentation and free space of the index. Anything above about 30% free space on a hot index is worth a REINDEX CONCURRENTLY. VACUUM alone will not shrink it.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"cover-letter\",\n      \"title\": \"Cover letter rewrite\",\n      \"preview\": \"Cut to half a page\",\n      \"updated\": \"5d\",\n      \"group\": \"week\",\n      \"turns\": [\n        {\n          \"id\": \"cl-1\",\n          \"role\": \"user\",\n          \"text\": \"My cover letter is a full page. What should I cut?\"\n        },\n        {\n          \"id\": \"cl-2\",\n          \"role\": \"assistant\",\n          \"thought\": 4,\n          \"reasoning\": \"Most cover letters repeat the resume. Cut everything that is already there and keep the one story that is not.\",\n          \"text\": \"Cut anything the resume already says. Keep one short story that shows how you work, one sentence on why this company, and a plain closing line. That is half a page.\"\n        }\n      ]\n    },\n    {\n      \"id\": \"rust\",\n      \"title\": \"Rust lifetimes explained\",\n      \"preview\": \"Borrow checker with two references\",\n      \"updated\": \"6d\",\n      \"group\": \"week\",\n      \"turns\": [\n        {\n          \"id\": \"rs-1\",\n          \"role\": \"user\",\n          \"text\": \"Why does the compiler want a lifetime on this function returning a reference?\"\n        },\n        {\n          \"id\": \"rs-2\",\n          \"role\": \"assistant\",\n          \"thought\": 7,\n          \"reasoning\": \"The function takes two references and returns one; the compiler cannot know which input the output borrows from. Explain elision rules briefly.\",\n          \"text\": \"Because the function takes two references and returns one, the compiler cannot tell which input the result borrows from. A lifetime annotation is you telling it. With a single reference input it would be inferred automatically.\"\n        }\n      ]\n    }\n  ]\n}\n"
        },
        {
          "path": "content/components/chat/chat-03/app/chat/page.tsx",
          "type": "registry:page",
          "target": "app/chat/page.tsx",
          "content": "'use client';\n\nimport {\n  IconCheck,\n  IconChevronDown,\n  IconLayoutSidebar,\n  IconShare,\n} from '@tabler/icons-react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Kbd } from '@/components/ui/kbd';\nimport {\n  SidebarInset,\n  SidebarProvider,\n  SidebarTrigger,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport { ChatSidebar } from '@/components/chat-03/chat-sidebar';\nimport { CommandMenu } from '@/components/chat-03/command-menu';\nimport { type Attachment, Composer } from '@/components/chat-03/composer';\nimport {\n  Conversation,\n  type Thread,\n  type Turn,\n} from '@/components/chat-03/conversation';\nimport { ClaudeLogo, OpenAILogo } from '@/components/chat-03/logos';\nimport data from './data.json' with { type: 'json' };\n\nconst { models, replies } = data;\nconst seed = data.threads as Thread[];\n\nconst logos = { openai: OpenAILogo, anthropic: ClaudeLogo };\nconst Logo = ({ vendor, ...props }: { vendor: string; className?: string }) => {\n  const Icon = logos[vendor as keyof typeof logos];\n  return <Icon {...props} />;\n};\n\nconst thinkDelay = 900;\nconst tokenDelay = 40;\n\nexport default function Chat03() {\n  const [threads, setThreads] = useState(seed);\n  const [activeId, setActiveId] = useState<string | null>('heat-pump');\n  const [draft, setDraft] = useState('');\n  const [attachment, setAttachment] = useState<Attachment | null>(null);\n  const [model, setModel] = useState(models[0].name);\n  const [search, setSearch] = useState(false);\n  const [replyIndex, setReplyIndex] = useState(0);\n  const timer = useRef<number | null>(null);\n\n  const thread = threads.find((t) => t.id === activeId) ?? null;\n  const streaming = thread?.turns.some((t) => t.streaming) ?? false;\n  const altModel = models.find((m) => m.name !== model)?.name ?? model;\n\n  const patch = (threadId: string, turnId: string, fn: (t: Turn) => Turn) =>\n    setThreads((prev) =>\n      prev.map((t) =>\n        t.id === threadId\n          ? { ...t, turns: t.turns.map((m) => (m.id === turnId ? fn(m) : m)) }\n          : t\n      )\n    );\n\n  const stream = (threadId: string, turnId: string) => {\n    const reply = replies[replyIndex % replies.length];\n    setReplyIndex(replyIndex + 1);\n    patch(threadId, turnId, (t) => ({\n      ...t,\n      text: '',\n      reasoning: reply.reasoning,\n      thought: undefined,\n      error: undefined,\n      streaming: true,\n    }));\n    let shown = 0;\n    const tick = () => {\n      shown = Math.min(\n        reply.text.length,\n        shown + 2 + Math.floor(Math.random() * 5)\n      );\n      const done = shown >= reply.text.length;\n      patch(threadId, turnId, (t) => ({\n        ...t,\n        text: reply.text.slice(0, shown),\n        thought: done ? 4 + Math.round(reply.reasoning.length / 40) : t.thought,\n        streaming: !done,\n      }));\n      timer.current = done\n        ? null\n        : window.setTimeout(tick, tokenDelay * (0.5 + Math.random()));\n    };\n    tick();\n  };\n\n  const stop = () => {\n    if (timer.current) {\n      window.clearTimeout(timer.current);\n      timer.current = null;\n    }\n    const live = thread?.turns.find((t) => t.streaming);\n    if (thread && live) {\n      patch(thread.id, live.id, (t) => ({\n        ...t,\n        streaming: false,\n        error: true,\n      }));\n    }\n  };\n\n  const send = () => {\n    const text = draft.trim();\n    const replyId = crypto.randomUUID();\n    const turns: Turn[] = [\n      { id: crypto.randomUUID(), role: 'user', text, day: 'today' },\n      {\n        id: replyId,\n        role: 'assistant',\n        text: '',\n        streaming: true,\n        day: 'today',\n      },\n    ];\n    const id = thread?.id ?? crypto.randomUUID();\n    setThreads((prev) =>\n      thread\n        ? [\n            {\n              ...thread,\n              updated: 'now',\n              group: 'today',\n              turns: [\n                ...thread.turns.map((t) => ({\n                  ...t,\n                  day: t.day ?? thread.group,\n                })),\n                ...turns,\n              ],\n            },\n            ...prev.filter((t) => t.id !== id),\n          ]\n        : [\n            {\n              id,\n              title: text.split(' ').slice(0, 5).join(' '),\n              preview: text,\n              updated: 'now',\n              group: 'today',\n              turns,\n            },\n            ...prev,\n          ]\n    );\n    setActiveId(id);\n    setDraft('');\n    setAttachment(null);\n    window.setTimeout(() => stream(id, replyId), thinkDelay);\n  };\n\n  const retry = (turnId: string, next?: string) => {\n    if (!thread) {\n      return;\n    }\n    if (next) {\n      setModel(next);\n    }\n    patch(thread.id, turnId, (t) => ({\n      ...t,\n      text: '',\n      thought: undefined,\n      error: undefined,\n      streaming: true,\n    }));\n    window.setTimeout(() => stream(thread.id, turnId), thinkDelay);\n  };\n\n  const edit = (turn: Turn) => {\n    if (!thread) {\n      return;\n    }\n    const at = thread.turns.findIndex((t) => t.id === turn.id);\n    setThreads((prev) =>\n      prev.map((t) =>\n        t.id === thread.id ? { ...t, turns: t.turns.slice(0, at) } : t\n      )\n    );\n    setDraft(turn.text);\n  };\n\n  return (\n    <SidebarProvider\n      className=\"h-dvh min-h-0\"\n      style={{ '--sidebar-width': '260px' } as React.CSSProperties}\n    >\n      <ChatSidebar\n        activeId={activeId}\n        onChange={(next) => {\n          setThreads(next);\n          if (activeId && !next.some((t) => t.id === activeId)) {\n            setActiveId(null);\n          }\n        }}\n        onNew={() => setActiveId(null)}\n        onSearch={() => setSearch(true)}\n        onSelect={setActiveId}\n        threads={threads}\n        user={data.user}\n      />\n      <SidebarInset className=\"min-h-0 bg-background\">\n        <header className=\"flex h-13 shrink-0 items-center gap-2.5 border-b px-4\">\n          <SidebarTrigger className=\"size-7.5 rounded-lg text-muted-foreground\">\n            <IconLayoutSidebar className=\"size-4\" stroke={1.8} />\n          </SidebarTrigger>\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              render={\n                <Button\n                  className=\"h-8 gap-1.5 rounded-lg px-2.5 text-[13px]\"\n                  variant=\"secondary\"\n                />\n              }\n            >\n              <Logo\n                className=\"size-3.5\"\n                vendor={\n                  models.find((m) => m.name === model)?.vendor ?? 'openai'\n                }\n              />\n              {model}\n              <IconChevronDown\n                className=\"size-3 text-muted-foreground\"\n                stroke={2}\n              />\n            </DropdownMenuTrigger>\n            <DropdownMenuContent\n              align=\"start\"\n              className=\"w-75 rounded-xl p-1.5\"\n              sideOffset={6}\n            >\n              <DropdownMenuGroup className=\"flex flex-col gap-0.5\">\n                {models.map((m) => (\n                  <DropdownMenuItem\n                    className={cn(\n                      'gap-2.5 rounded-md px-2.5 py-2',\n                      m.name === model && 'bg-muted'\n                    )}\n                    key={m.name}\n                    onClick={() => setModel(m.name)}\n                  >\n                    <span className=\"flex grow flex-col gap-0.5\">\n                      <span className=\"flex items-center gap-1.5 font-medium text-[13px]/4\">\n                        <Logo className=\"size-3 shrink-0\" vendor={m.vendor} />\n                        {m.name}\n                      </span>\n                      <span className=\"text-[11px]/3.5 text-muted-foreground\">\n                        {m.hint}\n                      </span>\n                    </span>\n                    {m.name === model && (\n                      <IconCheck className=\"size-3.5\" stroke={2.2} />\n                    )}\n                    <Kbd className=\"h-4.5 border border-border bg-background px-1.5 font-mono text-[10px]\">\n                      {m.context}\n                    </Kbd>\n                  </DropdownMenuItem>\n                ))}\n              </DropdownMenuGroup>\n            </DropdownMenuContent>\n          </DropdownMenu>\n          <div className=\"grow\" />\n          {thread && (\n            <Button\n              aria-label=\"Share thread\"\n              className=\"size-7.5 rounded-lg text-muted-foreground\"\n              size=\"icon-sm\"\n              variant=\"ghost\"\n            >\n              <IconShare className=\"size-4\" stroke={1.8} />\n            </Button>\n          )}\n        </header>\n\n        <Conversation\n          altModel={altModel}\n          onEdit={edit}\n          onOpen={setActiveId}\n          onRetry={retry}\n          recent={threads.filter((t) => !t.pinned).slice(0, 3)}\n          thread={thread}\n        />\n\n        <Composer\n          attachment={attachment}\n          draft={draft}\n          onAttachment={setAttachment}\n          onDraft={setDraft}\n          onSend={send}\n          onStop={stop}\n          streaming={streaming}\n        />\n      </SidebarInset>\n\n      <CommandMenu\n        onNew={(text) => {\n          setActiveId(null);\n          setDraft(text);\n        }}\n        onOpen={setActiveId}\n        onOpenChange={setSearch}\n        open={search}\n        threads={threads}\n      />\n    </SidebarProvider>\n  );\n}\n"
        }
      ],
      "categories": [
        "chat"
      ]
    },
    {
      "name": "command-menu-01",
      "type": "registry:block",
      "title": "Command Menu with Groups",
      "description": "A command menu with groups block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "command",
        "kbd"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/command-menu/command-menu-01.tsx",
          "type": "registry:component",
          "target": "components/command-menu-01.tsx",
          "content": "'use client';\n\nimport {\n  IconBell,\n  IconBolt,\n  IconCalendar,\n  IconChartBar,\n  IconChartPie,\n  IconClock,\n  IconFileText,\n  IconHelp,\n  IconKeyboard,\n  IconLayoutDashboard,\n  IconLayoutKanban,\n  IconLogout,\n  IconMessage,\n  IconPalette,\n  IconSettings,\n  IconSquareCheck,\n  IconTarget,\n  IconTrendingUp,\n  IconUsers,\n} from '@tabler/icons-react';\nimport { useEffect, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Command,\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from '@/components/ui/command';\nimport { Kbd } from '@/components/ui/kbd';\n\nconst workspaceItems = [\n  { icon: IconLayoutDashboard, label: 'Dashboard' },\n  { icon: IconLayoutKanban, label: 'Projects' },\n  { icon: IconSquareCheck, label: 'Tasks' },\n  { icon: IconCalendar, label: 'Calendar' },\n  { icon: IconUsers, label: 'Team members' },\n  { icon: IconMessage, label: 'Messages' },\n  { icon: IconFileText, label: 'Documents' },\n  { icon: IconBell, label: 'Notifications' },\n  { icon: IconClock, label: 'Time tracking' },\n  { icon: IconTarget, label: 'Goals' },\n];\n\nconst analyticsItems = [\n  { icon: IconChartBar, label: 'Overview' },\n  { icon: IconTrendingUp, label: 'Performance' },\n  { icon: IconChartPie, label: 'Reports' },\n  { icon: IconBolt, label: 'Insights' },\n];\n\nconst settingsItems = [\n  { icon: IconSettings, label: 'Preferences' },\n  { icon: IconPalette, label: 'Appearance' },\n  { icon: IconKeyboard, label: 'Keyboard shortcuts' },\n  { icon: IconHelp, label: 'Help & support' },\n  { icon: IconLogout, label: 'Sign out' },\n];\n\nexport function CommandMenu01() {\n  const [open, setOpen] = useState(true);\n\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault();\n        setOpen((prev) => !prev);\n      }\n    };\n\n    document.addEventListener('keydown', down);\n    return () => document.removeEventListener('keydown', down);\n  }, []);\n\n  return (\n    <>\n      <Button onClick={() => setOpen(true)} variant=\"outline\">\n        Open Command Menu\n      </Button>\n\n      <CommandDialog\n        className=\"-translate-y-1/2 top-1/2\"\n        onOpenChange={setOpen}\n        open={open}\n        showCloseButton={false}\n      >\n        <Command>\n          <CommandInput\n            className=\"h-12\"\n            placeholder=\"Type a command or search...\"\n          />\n          <CommandList className=\"h-[320px] max-h-[320px]\">\n            <CommandEmpty>No results found.</CommandEmpty>\n            <CommandGroup heading=\"Workspace\">\n              {workspaceItems.map((item) => (\n                <CommandItem key={item.label}>\n                  <item.icon className=\"mr-2 h-5 w-5\" />\n                  <span>{item.label}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n            <CommandGroup heading=\"Analytics\">\n              {analyticsItems.map((item) => (\n                <CommandItem key={item.label}>\n                  <item.icon className=\"mr-2 h-5 w-5\" />\n                  <span>{item.label}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n            <CommandGroup heading=\"Settings\">\n              {settingsItems.map((item) => (\n                <CommandItem key={item.label}>\n                  <item.icon className=\"mr-2 h-5 w-5\" />\n                  <span>{item.label}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n          </CommandList>\n          <div className=\"flex h-12 items-center justify-end border-t px-3\">\n            <button\n              className=\"flex items-center gap-1 text-muted-foreground text-sm hover:text-foreground\"\n              onClick={() => setOpen(false)}\n              type=\"button\"\n            >\n              <span>Close</span>\n              <Kbd className=\"ml-1\">Esc</Kbd>\n            </button>\n          </div>\n        </Command>\n      </CommandDialog>\n    </>\n  );\n}\n"
        }
      ],
      "categories": [
        "command-menu"
      ]
    },
    {
      "name": "command-menu-02",
      "type": "registry:block",
      "title": "Command Menu with Keyboard Shortcuts",
      "description": "A command menu with keyboard shortcuts block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "command",
        "dialog",
        "kbd"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/command-menu/command-menu-02.tsx",
          "type": "registry:component",
          "target": "components/command-menu-02.tsx",
          "content": "'use client';\n\nimport {\n  IconArrowRight,\n  IconAt,\n  IconCopy,\n  IconDeviceDesktop,\n  IconDownload,\n  IconFile,\n  IconFileSearch,\n  IconKeyboard,\n  IconLink,\n  IconLogout,\n  IconMessage,\n  IconPencil,\n  IconPlus,\n  IconSend,\n  IconSettings,\n  IconTemplate,\n  IconUser,\n  IconUsers,\n} from '@tabler/icons-react';\nimport { useEffect, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandShortcut,\n} from '@/components/ui/command';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog';\nimport { Kbd, KbdGroup } from '@/components/ui/kbd';\n\nexport function CommandMenu02() {\n  const [open, setOpen] = useState(true);\n  const [inputValue, setInputValue] = useState('');\n\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault();\n        setOpen((prev) => !prev);\n      }\n    };\n\n    document.addEventListener('keydown', down);\n    return () => document.removeEventListener('keydown', down);\n  }, []);\n\n  return (\n    <>\n      <Button onClick={() => setOpen(true)} variant=\"outline\">\n        Open Command Menu\n      </Button>\n\n      <Dialog onOpenChange={setOpen} open={open}>\n        <DialogHeader className=\"sr-only\">\n          <DialogTitle>Command Menu</DialogTitle>\n          <DialogDescription>\n            Use the command menu to navigate through the app.\n          </DialogDescription>\n        </DialogHeader>\n        <DialogContent\n          className=\"gap-0 overflow-hidden rounded-xl border-border/50 p-0 shadow-lg sm:max-w-lg\"\n          showCloseButton={false}\n        >\n          <Command className=\"p-0 **:data-[slot=input-group]:h-10! **:data-[slot=command-input-wrapper]:grow **:data-[slot=input-group]:rounded-none! **:data-[slot=input-group]:border-0 **:data-[slot=input-group]:bg-transparent **:data-[slot=command-input-wrapper]:p-0 **:data-[slot=input-group-addon]:pl-0!\">\n            <div className=\"flex h-12 items-center gap-2 border-border/50 border-b px-4\">\n              <CommandInput\n                className=\"text-[15px]\"\n                onValueChange={setInputValue}\n                placeholder=\"What do you need?\"\n                value={inputValue}\n              />\n              <button\n                className=\"flex shrink-0 items-center\"\n                onClick={() => setOpen(false)}\n                type=\"button\"\n              >\n                <Kbd>Esc</Kbd>\n              </button>\n            </div>\n\n            <CommandList className=\"max-h-[400px] py-2\">\n              <CommandEmpty>No results found.</CommandEmpty>\n\n              <CommandGroup>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconSettings aria-hidden />\n                  Account Settings...\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>,</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconUser aria-hidden />\n                  Switch Workspace...\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconLogout aria-hidden />\n                  Log Out\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>Q</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"Documents\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconFile aria-hidden />\n                  Search Documents...\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>F</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconPlus aria-hidden />\n                  Create New Document...\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>N</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconFile aria-hidden />\n                  Upload Document...\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>U</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"Signing\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconSend aria-hidden />\n                  Request Signature...\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconPencil aria-hidden />\n                  Sign a Document...\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconUsers aria-hidden />\n                  Bulk Send for Signature...\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"Templates\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconTemplate aria-hidden />\n                  Search Templates...\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconPlus aria-hidden />\n                  Create New Template...\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"General\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconDeviceDesktop aria-hidden />\n                  Change Theme...\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>T</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconCopy aria-hidden />\n                  Copy Current URL\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>⇧</Kbd>\n                      <Kbd>C</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"Navigation\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;<strong className=\"font-semibold\">Inbox</strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;\n                    <strong className=\"font-semibold\">Action Required</strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;\n                    <strong className=\"font-semibold\">\n                      Waiting for Others\n                    </strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;\n                    <strong className=\"font-semibold\">Completed</strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;<strong className=\"font-semibold\">Drafts</strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;\n                    <strong className=\"font-semibold\">Templates</strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;\n                    <strong className=\"font-semibold\">Archive</strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;<strong className=\"font-semibold\">Trash</strong>\n                  </span>\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconArrowRight aria-hidden />\n                  <span>\n                    Go to&nbsp;\n                    <strong className=\"font-semibold\">Settings</strong>\n                  </span>\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"Quick Actions\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconLink aria-hidden />\n                  Copy Signing Link\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconDownload aria-hidden />\n                  Download Document\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"Help\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconFileSearch aria-hidden />\n                  Search Help Center...\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconMessage aria-hidden />\n                  Send Feedback...\n                </CommandItem>\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconAt aria-hidden />\n                  Contact Support\n                </CommandItem>\n              </CommandGroup>\n\n              <CommandGroup heading=\"Keyboard Shortcuts\">\n                <CommandItem\n                  className=\"mx-2 rounded-lg py-2.5\"\n                  onSelect={() => setOpen(false)}\n                >\n                  <IconKeyboard aria-hidden />\n                  View Keyboard Shortcuts\n                  <CommandShortcut>\n                    <KbdGroup>\n                      <Kbd>⌘</Kbd>\n                      <Kbd>/</Kbd>\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n              </CommandGroup>\n            </CommandList>\n          </Command>\n        </DialogContent>\n      </Dialog>\n    </>\n  );\n}\n"
        }
      ],
      "categories": [
        "command-menu"
      ]
    },
    {
      "name": "command-menu-03",
      "type": "registry:block",
      "title": "Command Menu with Documentation Search",
      "description": "A command menu with documentation search block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "command",
        "dialog",
        "kbd"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/command-menu/command-menu-03.tsx",
          "type": "registry:component",
          "target": "components/command-menu-03.tsx",
          "content": "'use client';\n\nimport { IconArrowRight, IconCornerDownLeft } from '@tabler/icons-react';\nimport { useRouter } from 'next/navigation';\nimport { useCallback, useEffect, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandShortcut,\n} from '@/components/ui/command';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog';\nimport { Kbd } from '@/components/ui/kbd';\n\nconst navItems = [\n  { href: '/', label: 'Home', keywords: ['home', 'main', 'index'] },\n  {\n    href: '/dashboard',\n    label: 'Dashboard',\n    keywords: ['dashboard', 'overview'],\n  },\n  {\n    href: '/settings',\n    label: 'Settings',\n    keywords: ['settings', 'preferences'],\n  },\n];\n\nconst pageGroups = [\n  {\n    name: 'Getting Started',\n    pages: [\n      {\n        name: 'Introduction',\n        href: '/docs/introduction',\n        keywords: ['intro', 'start'],\n      },\n      {\n        name: 'Installation',\n        href: '/docs/installation',\n        keywords: ['install', 'setup'],\n      },\n      {\n        name: 'Quick Start',\n        href: '/docs/quick-start',\n        keywords: ['quick', 'begin'],\n      },\n    ],\n  },\n  {\n    name: 'Utilities',\n    pages: [\n      {\n        name: 'Typography',\n        href: '/docs/utilities/typography',\n        keywords: ['text', 'font'],\n      },\n      {\n        name: 'Colors',\n        href: '/docs/utilities/colors',\n        keywords: ['color', 'theme'],\n      },\n      {\n        name: 'Spacing',\n        href: '/docs/utilities/spacing',\n        keywords: ['margin', 'padding'],\n      },\n    ],\n  },\n];\n\nconst colorGroups = [\n  {\n    name: 'Neutral',\n    colors: [\n      {\n        name: 'Neutral 50',\n        className: 'neutral-50',\n        value: 'oklch(0.985 0 0)',\n      },\n      {\n        name: 'Neutral 100',\n        className: 'neutral-100',\n        value: 'oklch(0.97 0 0)',\n      },\n      {\n        name: 'Neutral 200',\n        className: 'neutral-200',\n        value: 'oklch(0.922 0 0)',\n      },\n      {\n        name: 'Neutral 500',\n        className: 'neutral-500',\n        value: 'oklch(0.556 0 0)',\n      },\n      {\n        name: 'Neutral 900',\n        className: 'neutral-900',\n        value: 'oklch(0.205 0 0)',\n      },\n    ],\n  },\n  {\n    name: 'Blue',\n    colors: [\n      {\n        name: 'Blue 50',\n        className: 'blue-50',\n        value: 'oklch(0.97 0.014 254.604)',\n      },\n      {\n        name: 'Blue 500',\n        className: 'blue-500',\n        value: 'oklch(0.623 0.214 259.815)',\n      },\n      {\n        name: 'Blue 600',\n        className: 'blue-600',\n        value: 'oklch(0.546 0.245 262.881)',\n      },\n    ],\n  },\n];\n\nexport function CommandMenu03() {\n  const router = useRouter();\n  const [open, setOpen] = useState(true);\n\n  const copyToClipboard = useCallback((text: string) => {\n    navigator.clipboard.writeText(text);\n  }, []);\n\n  const runCommand = useCallback((command: () => unknown) => {\n    setOpen(false);\n    command();\n  }, []);\n\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if ((e.key === 'k' && (e.metaKey || e.ctrlKey)) || e.key === '/') {\n        if (\n          (e.target instanceof HTMLElement && e.target.isContentEditable) ||\n          e.target instanceof HTMLInputElement ||\n          e.target instanceof HTMLTextAreaElement ||\n          e.target instanceof HTMLSelectElement\n        ) {\n          return;\n        }\n        e.preventDefault();\n        setOpen((prev) => !prev);\n      }\n    };\n\n    document.addEventListener('keydown', down);\n    return () => document.removeEventListener('keydown', down);\n  }, []);\n\n  return (\n    <>\n      <Button onClick={() => setOpen(true)} variant=\"outline\">\n        Open Command Menu\n      </Button>\n\n      <Dialog onOpenChange={setOpen} open={open}>\n        <DialogContent className=\"rounded-xl border-none bg-clip-padding p-2 pb-11 shadow-2xl ring-4 ring-neutral-200/80 **:data-[slot=dialog-close]:top-3! **:data-[slot=dialog-close]:right-3! sm:max-w-md dark:bg-neutral-900 dark:ring-neutral-800\">\n          <DialogHeader className=\"sr-only\">\n            <DialogTitle>Search documentation...</DialogTitle>\n            <DialogDescription>\n              Search for a command to run...\n            </DialogDescription>\n          </DialogHeader>\n\n          <Command className=\"rounded-none! bg-transparent p-0 **:data-[slot=input-group]:h-9! **:data-[slot=input-group]:rounded-md! **:data-[slot=input-group]:border-input **:data-[slot=input-group]:bg-input/50 **:data-[slot=command-input-wrapper]:p-0 **:data-[slot=command-input]:pr-9 **:data-[slot=command-input]:pl-2! **:data-[slot=input-group-addon]:pl-3!\">\n            <CommandInput placeholder=\"Search documentation...\" />\n            <CommandList className=\"no-scrollbar min-h-80 scroll-pt-2 scroll-pb-1.5\">\n              <CommandEmpty className=\"py-12 text-center text-muted-foreground text-sm\">\n                No results found.\n              </CommandEmpty>\n\n              {navItems.length > 0 && (\n                <CommandGroup\n                  className=\"p-0! **:[[cmdk-group-heading]]:scroll-mt-16 **:[[cmdk-group-heading]]:p-3! **:[[cmdk-group-heading]]:pb-1!\"\n                  heading=\"Pages\"\n                >\n                  {navItems.map((item) => (\n                    <CommandItem\n                      className=\"h-9 rounded-md border border-transparent px-3! font-medium hover:border-input hover:bg-input/50\"\n                      key={item.href}\n                      keywords={item.keywords}\n                      onSelect={() => {\n                        runCommand(() => router.push(item.href));\n                      }}\n                      value={`Navigation ${item.label}`}\n                    >\n                      <IconArrowRight aria-hidden=\"true\" className=\"size-4\" />\n                      {item.label}\n                    </CommandItem>\n                  ))}\n                </CommandGroup>\n              )}\n\n              {pageGroups.map((group) => (\n                <CommandGroup\n                  className=\"p-0! **:[[cmdk-group-heading]]:scroll-mt-16 **:[[cmdk-group-heading]]:p-3! **:[[cmdk-group-heading]]:pb-1!\"\n                  heading={group.name}\n                  key={group.name}\n                >\n                  {group.pages.map((page) => {\n                    const isComponent = page.href.includes('/components/');\n                    return (\n                      <CommandItem\n                        className=\"h-9 rounded-md border border-transparent px-3! font-medium hover:border-input hover:bg-input/50\"\n                        key={page.href}\n                        keywords={page.keywords}\n                        onSelect={() => {\n                          runCommand(() => router.push(page.href));\n                        }}\n                        value={`${group.name} ${page.name}`}\n                      >\n                        {isComponent ? (\n                          <div className=\"aspect-square size-4 rounded-full border border-muted-foreground border-dashed\" />\n                        ) : (\n                          <IconArrowRight\n                            aria-hidden=\"true\"\n                            className=\"size-4\"\n                          />\n                        )}\n                        {page.name}\n                      </CommandItem>\n                    );\n                  })}\n                </CommandGroup>\n              ))}\n\n              {colorGroups.map((colorGroup) => (\n                <CommandGroup\n                  className=\"p-0! **:[[cmdk-group-heading]]:p-3!\"\n                  heading={colorGroup.name}\n                  key={colorGroup.name}\n                >\n                  {colorGroup.colors.map((color) => (\n                    <CommandItem\n                      className=\"h-9 rounded-md border border-transparent px-3! font-medium hover:border-input hover:bg-input/50\"\n                      key={color.className}\n                      keywords={['color', color.name, color.className]}\n                      onSelect={() => {\n                        runCommand(() => copyToClipboard(color.value));\n                      }}\n                      value={color.className}\n                    >\n                      <div\n                        className=\"aspect-square size-4 rounded-sm border\"\n                        style={{ backgroundColor: color.value }}\n                      />\n                      {color.className}\n                      <CommandShortcut className=\"font-mono font-normal text-xs tabular-nums tracking-normal\">\n                        {color.value}\n                      </CommandShortcut>\n                    </CommandItem>\n                  ))}\n                </CommandGroup>\n              ))}\n            </CommandList>\n          </Command>\n\n          <div className=\"absolute inset-x-0 bottom-0 z-20 flex h-10 items-center gap-2 rounded-b-xl border-t border-t-neutral-100 bg-neutral-50 px-4 font-medium text-muted-foreground text-xs dark:border-t-neutral-700 dark:bg-neutral-800\">\n            <Kbd>\n              <IconCornerDownLeft aria-hidden=\"true\" className=\"size-3\" />\n            </Kbd>\n            Select\n          </div>\n        </DialogContent>\n      </Dialog>\n    </>\n  );\n}\n"
        }
      ],
      "categories": [
        "command-menu"
      ]
    },
    {
      "name": "command-menu-04",
      "type": "registry:block",
      "title": "Command Menu with Global Search",
      "description": "A command menu with global search block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "command"
      ],
      "dependencies": [
        "@tabler/icons-react",
        "cmdk"
      ],
      "files": [
        {
          "path": "content/components/command-menu/command-menu-04.tsx",
          "type": "registry:component",
          "target": "components/command-menu-04.tsx",
          "content": "'use client';\n\nimport {\n  IconFileText,\n  IconFileTypeDocx,\n  IconFileTypePdf,\n  IconFileTypeXls,\n  IconFileZip,\n  IconFolder,\n  IconMessageCircle,\n  IconPhoto,\n  IconX,\n} from '@tabler/icons-react';\nimport { Command as CommandPrimitive } from 'cmdk';\nimport { type ComponentType, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Command,\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandItem,\n  CommandList,\n} from '@/components/ui/command';\nimport { cn } from '@/lib/utils';\n\nexport type SearchResultKind = 'chat' | 'image' | 'document' | 'project';\n\nexport type SearchResult = {\n  id: string;\n  kind: SearchResultKind;\n  title: string;\n  /** Shown under the title while searching. Chats use a text excerpt; files use their type. */\n  snippet: string;\n  date: string;\n  /** Images only: CSS background for the thumbnail. */\n  thumbnail?: string;\n};\n\ntype Filter = 'all' | SearchResultKind;\n\nconst filters: { value: Filter; label: string }[] = [\n  { value: 'all', label: 'All' },\n  { value: 'chat', label: 'Chats' },\n  { value: 'image', label: 'Images' },\n  { value: 'document', label: 'Documents' },\n  { value: 'project', label: 'Projects' },\n];\n\nconst fileIcons: Record<string, ComponentType<{ className?: string }>> = {\n  pdf: IconFileTypePdf,\n  docx: IconFileTypeDocx,\n  xlsx: IconFileTypeXls,\n  zip: IconFileZip,\n};\n\nconst kindIcons: Record<\n  SearchResultKind,\n  ComponentType<{ className?: string }>\n> = {\n  chat: IconMessageCircle,\n  image: IconPhoto,\n  document: IconFileText,\n  project: IconFolder,\n};\n\nconst lastOpenedChats: SearchResult[] = [\n  {\n    id: 'c1',\n    kind: 'chat',\n    title: 'Q3 launch checklist',\n    snippet: 'Pin the launch date once legal signs off on the new terms.',\n    date: 'Today',\n  },\n  {\n    id: 'c2',\n    kind: 'chat',\n    title: 'Draft onboarding email sequence',\n    snippet:\n      'Three emails over five days, starting the morning after launch, each with one clear action.',\n    date: 'Today',\n  },\n];\n\nconst recentChats: SearchResult[] = [\n  {\n    id: 'c3',\n    kind: 'chat',\n    title: 'Compare pricing page layouts',\n    snippet:\n      'Three-column pricing works when the middle tier is the one most people should pick. Ship it with the launch.',\n    date: 'Yesterday',\n  },\n  {\n    id: 'c4',\n    kind: 'chat',\n    title: 'Summarize customer interviews',\n    snippet:\n      'Eight of twelve customers mentioned invoice export first. Nobody brought up the dashboard redesign.',\n    date: 'Yesterday',\n  },\n  {\n    id: 'c5',\n    kind: 'chat',\n    title: 'Fix flaky checkout test',\n    snippet:\n      'The test races the payment webhook. Wait for the order status instead of a fixed delay.',\n    date: 'Sep 4',\n  },\n  {\n    id: 'c6',\n    kind: 'chat',\n    title: 'Write release notes for v2.4',\n    snippet:\n      'Lead with the two launch features people asked for most, then list fixes in one short block.',\n    date: 'Sep 2',\n  },\n  {\n    id: 'c7',\n    kind: 'chat',\n    title: 'Plan database index cleanup',\n    snippet:\n      'Drop the four unused indexes on orders first; they cost the most on writes.',\n    date: 'Aug 30',\n  },\n  {\n    id: 'c8',\n    kind: 'chat',\n    title: 'Brainstorm team offsite agenda',\n    snippet:\n      'Half a day on the roadmap, half a day on the launch retro, evening free.',\n    date: 'Aug 28',\n  },\n  {\n    id: 'c9',\n    kind: 'chat',\n    title: 'Review API rate limit options',\n    snippet:\n      'Token bucket per API key, 600 requests a minute. Raise the ceiling for launch week.',\n    date: 'Aug 26',\n  },\n];\n\nconst images: SearchResult[] = [\n  {\n    id: 'i1',\n    kind: 'image',\n    title: 'launch-hero-banner.png',\n    snippet: 'Image',\n    date: 'Sep 3',\n    thumbnail: 'linear-gradient(135deg, #f97316, #db2777)',\n  },\n  {\n    id: 'i2',\n    kind: 'image',\n    title: 'launch-pricing-table.png',\n    snippet: 'Image',\n    date: 'Aug 20',\n    thumbnail: 'linear-gradient(135deg, #0ea5e9, #6366f1)',\n  },\n  {\n    id: 'i3',\n    kind: 'image',\n    title: 'launch-onboarding-flow.png',\n    snippet: 'Image',\n    date: 'Aug 27',\n    thumbnail: 'linear-gradient(135deg, #22c55e, #0d9488)',\n  },\n  {\n    id: 'i4',\n    kind: 'image',\n    title: 'dashboard-dark-mode.png',\n    snippet: 'Image',\n    date: 'Aug 15',\n    thumbnail: 'linear-gradient(135deg, #1e293b, #475569)',\n  },\n  {\n    id: 'i5',\n    kind: 'image',\n    title: 'launch-social-card.jpg',\n    snippet: 'Image',\n    date: 'Aug 12',\n    thumbnail: 'linear-gradient(135deg, #a855f7, #f43f5e)',\n  },\n  {\n    id: 'i6',\n    kind: 'image',\n    title: 'checkout-error-states.png',\n    snippet: 'Image',\n    date: 'Jul 30',\n    thumbnail: 'linear-gradient(135deg, #eab308, #f97316)',\n  },\n];\n\nconst documents: SearchResult[] = [\n  {\n    id: 'd1',\n    kind: 'document',\n    title: 'Q3_launch_plan.pdf',\n    snippet: 'PDF',\n    date: 'Sep 1',\n  },\n  {\n    id: 'd2',\n    kind: 'document',\n    title: 'launch-pricing-proposal.docx',\n    snippet: 'Document',\n    date: 'Aug 22',\n  },\n  {\n    id: 'd3',\n    kind: 'document',\n    title: 'pre-launch-interview-notes.md',\n    snippet: 'Document',\n    date: 'Aug 19',\n  },\n  {\n    id: 'd4',\n    kind: 'document',\n    title: 'launch-assets-final.zip',\n    snippet: 'File',\n    date: 'Aug 11',\n  },\n  {\n    id: 'd5',\n    kind: 'document',\n    title: 'pricing-experiments.xlsx',\n    snippet: 'Spreadsheet',\n    date: 'Aug 14',\n  },\n];\n\nconst projects: SearchResult[] = [\n  {\n    id: 'p1',\n    kind: 'project',\n    title: 'Q3 product launch',\n    snippet: 'Project',\n    date: 'Sep 5',\n  },\n  {\n    id: 'p2',\n    kind: 'project',\n    title: 'Launch pricing and packaging',\n    snippet: 'Project',\n    date: 'Aug 30',\n  },\n  {\n    id: 'p3',\n    kind: 'project',\n    title: 'Post-launch onboarding redesign',\n    snippet: 'Project',\n    date: 'Aug 21',\n  },\n  {\n    id: 'p4',\n    kind: 'project',\n    title: 'Checkout reliability',\n    snippet: 'Project',\n    date: 'Jul 28',\n  },\n];\n\nconst searchResults: SearchResult[] = [\n  ...lastOpenedChats,\n  ...recentChats,\n  ...images,\n  ...documents,\n  ...projects,\n];\n\nexport function CommandMenu04() {\n  const [open, setOpen] = useState(true);\n\n  return (\n    <>\n      <Button onClick={() => setOpen(true)} variant=\"outline\">\n        Search\n      </Button>\n\n      <GlobalSearch\n        lastOpened={lastOpenedChats}\n        onOpenChange={setOpen}\n        onSelect={() => setOpen(false)}\n        open={open}\n        recent={recentChats}\n        results={searchResults}\n      />\n    </>\n  );\n}\n\nexport function GlobalSearch({\n  open,\n  onOpenChange,\n  onSelect,\n  lastOpened,\n  recent,\n  results,\n}: {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  onSelect: (result: SearchResult) => void;\n  lastOpened: SearchResult[];\n  recent: SearchResult[];\n  results: SearchResult[];\n}) {\n  const [query, setQuery] = useState('');\n  const [filter, setFilter] = useState<Filter>('all');\n  const needle = query.trim().toLowerCase();\n\n  const matches = results.filter(\n    (result) =>\n      (filter === 'all' || result.kind === filter) &&\n      `${result.title} ${result.snippet}`.toLowerCase().includes(needle)\n  );\n\n  const reset = () => {\n    setQuery('');\n    setFilter('all');\n  };\n\n  return (\n    <CommandDialog\n      className=\"-translate-y-1/2 top-1/2 rounded-2xl! sm:max-w-[720px]\"\n      description=\"Search chats, images, documents, and projects\"\n      onOpenChange={(next) => {\n        if (!next) {\n          reset();\n        }\n        onOpenChange(next);\n      }}\n      open={open}\n      title=\"Global search\"\n    >\n      <Command shouldFilter={false}>\n        <div className=\"flex h-16 shrink-0 items-center py-5 pr-4 pl-6\">\n          <CommandPrimitive.Input\n            className=\"min-w-0 flex-1 bg-transparent text-base tracking-[-0.02em] outline-hidden placeholder:text-muted-foreground\"\n            onValueChange={setQuery}\n            placeholder=\"Search...\"\n            value={query}\n          />\n          <div className=\"flex shrink-0 items-center gap-1\">\n            {query && (\n              <>\n                <button\n                  className=\"px-2 text-muted-foreground text-sm hover:text-foreground\"\n                  onClick={reset}\n                  type=\"button\"\n                >\n                  Clear\n                </button>\n                <span aria-hidden className=\"mx-1 h-5 w-px bg-border\" />\n              </>\n            )}\n            <button\n              aria-label=\"Close search\"\n              className=\"flex size-9 items-center justify-center rounded-full text-muted-foreground hover:bg-muted hover:text-foreground\"\n              onClick={() => onOpenChange(false)}\n              type=\"button\"\n            >\n              <IconX className=\"size-5\" />\n            </button>\n          </div>\n        </div>\n\n        <CommandList className=\"h-[396px] max-h-[396px] pb-3\">\n          {needle ? (\n            <>\n              <div\n                aria-label=\"Result type filters\"\n                className=\"sticky top-0 z-10 flex gap-1 bg-popover px-6 pb-3\"\n                role=\"tablist\"\n              >\n                {filters.map((option) => {\n                  const selected = option.value === filter;\n                  return (\n                    <button\n                      aria-selected={selected}\n                      className={cn(\n                        'rounded-full px-3.5 py-1.5 text-sm transition-colors',\n                        selected\n                          ? 'bg-muted text-foreground'\n                          : 'text-muted-foreground hover:text-foreground'\n                      )}\n                      key={option.value}\n                      onClick={() => setFilter(option.value)}\n                      role=\"tab\"\n                      type=\"button\"\n                    >\n                      {option.label}\n                    </button>\n                  );\n                })}\n              </div>\n              <CommandEmpty>No results for “{query}”.</CommandEmpty>\n              {filter === 'image' ? (\n                <div className=\"grid grid-cols-4 gap-3 px-6\">\n                  {matches.map((result) => (\n                    <CommandItem\n                      className=\"block aspect-square rounded-xl! bg-transparent! p-0 [&_svg]:hidden\"\n                      key={result.id}\n                      onSelect={() => onSelect(result)}\n                      value={result.id}\n                    >\n                      <span\n                        aria-label={result.title}\n                        className=\"block size-full rounded-xl ring-1 ring-foreground/10 ring-offset-2 ring-offset-popover group-data-selected/command-item:ring-foreground/40\"\n                        role=\"img\"\n                        style={{ background: result.thumbnail }}\n                      />\n                    </CommandItem>\n                  ))}\n                </div>\n              ) : (\n                matches.map((result) => (\n                  <ResultRow\n                    key={result.id}\n                    needle={needle}\n                    onSelect={onSelect}\n                    result={result}\n                  />\n                ))\n              )}\n            </>\n          ) : (\n            <>\n              <CommandGroup\n                className=\"p-0 **:[[cmdk-group-heading]]:px-6 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:font-normal! **:[[cmdk-group-heading]]:text-sm!\"\n                heading=\"Last opened\"\n              >\n                {lastOpened.map((result) => (\n                  <ResultRow\n                    key={result.id}\n                    onSelect={onSelect}\n                    result={result}\n                  />\n                ))}\n              </CommandGroup>\n              <CommandGroup\n                className=\"mt-5 p-0 **:[[cmdk-group-heading]]:px-6 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:font-normal! **:[[cmdk-group-heading]]:text-sm!\"\n                heading=\"Recent chats\"\n              >\n                {recent.map((result) => (\n                  <ResultRow\n                    key={result.id}\n                    onSelect={onSelect}\n                    result={result}\n                  />\n                ))}\n              </CommandGroup>\n            </>\n          )}\n        </CommandList>\n      </Command>\n    </CommandDialog>\n  );\n}\n\nfunction ResultRow({\n  result,\n  needle,\n  onSelect,\n}: {\n  result: SearchResult;\n  needle?: string;\n  onSelect: (result: SearchResult) => void;\n}) {\n  const extension = result.title.split('.').pop() ?? '';\n  const Icon =\n    result.kind === 'document'\n      ? (fileIcons[extension] ?? IconFileText)\n      : kindIcons[result.kind];\n\n  return (\n    <CommandItem\n      className=\"mx-3 min-h-12 gap-3 rounded-xl! px-3 py-2 text-sm\"\n      onSelect={() => onSelect(result)}\n      value={result.id}\n    >\n      <span className=\"flex size-8 shrink-0 items-center justify-center\">\n        <Icon\n          className={cn('size-5!', extension === 'pdf' && 'text-red-500')}\n        />\n      </span>\n      <span className=\"min-w-0 flex-1\">\n        <span className=\"block truncate leading-5\">\n          <Highlight needle={needle} text={result.title} />\n        </span>\n        {needle && (\n          <span className=\"block truncate pt-0.5 text-muted-foreground leading-5\">\n            <Highlight needle={needle} text={result.snippet} />\n          </span>\n        )}\n      </span>\n      {needle && (\n        <span className=\"shrink-0 pl-4 text-muted-foreground text-sm\">\n          {result.date}\n        </span>\n      )}\n    </CommandItem>\n  );\n}\n\nfunction Highlight({ text, needle }: { text: string; needle?: string }) {\n  const start = needle ? text.toLowerCase().indexOf(needle) : -1;\n  if (!needle || start === -1) {\n    return text;\n  }\n  const end = start + needle.length;\n  return (\n    <>\n      {text.slice(0, start)}\n      <mark className=\"bg-transparent font-semibold text-foreground\">\n        {text.slice(start, end)}\n      </mark>\n      {text.slice(end)}\n    </>\n  );\n}\n"
        }
      ],
      "categories": [
        "command-menu"
      ]
    },
    {
      "name": "dashboard-01",
      "type": "registry:block",
      "title": "Dashboard with Revenue and Customers",
      "description": "A dashboard with revenue and customers block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "button",
        "chart",
        "dialog",
        "dropdown-menu",
        "field",
        "input",
        "input-group",
        "popover",
        "progress",
        "separator",
        "sidebar",
        "table",
        "toggle-group"
      ],
      "dependencies": [
        "date-fns",
        "lucide-react",
        "recharts"
      ],
      "files": [
        {
          "path": "content/components/dashboard/dashboard-01/dashboard-header.tsx",
          "type": "registry:component",
          "target": "components/dashboard-01/dashboard-header.tsx",
          "content": "'use client';\n\nimport { format, parseISO } from 'date-fns';\nimport {\n  Bell,\n  CalendarDays,\n  ChartNoAxesCombined,\n  ChevronDown,\n  LockKeyhole,\n  LogOut,\n  Moon,\n  PanelLeftClose,\n  PanelLeftOpen,\n  Sun,\n  UserRound,\n} from 'lucide-react';\nimport { type ComponentProps, useState } from 'react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Field, FieldGroup, FieldLabel } from '@/components/ui/field';\nimport { Input } from '@/components/ui/input';\nimport {\n  Popover,\n  PopoverContent,\n  PopoverDescription,\n  PopoverTitle,\n  PopoverTrigger,\n} from '@/components/ui/popover';\nimport { Separator } from '@/components/ui/separator';\nimport { useSidebar } from '@/components/ui/sidebar';\nimport { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';\nimport { cn } from '@/lib/utils';\nimport type { DashboardCharts } from '@/components/dashboard-01/dashboard-charts';\n\ntype ChartOptions = ComponentProps<typeof DashboardCharts>;\ntype DateRange = Pick<ChartOptions, 'from' | 'to'>;\n\nconst press =\n  'ease-[cubic-bezier(0.23,1,0.32,1)] transition-[scale,background-color] duration-150 active:scale-[0.96] motion-reduce:active:scale-100';\n\nexport function DashboardHeader({\n  period,\n  range,\n  onPeriodChange,\n  onRangeChange,\n}: {\n  period: ChartOptions['period'];\n  range: DateRange;\n  onPeriodChange: (period: ChartOptions['period']) => void;\n  onRangeChange: (range: DateRange) => void;\n}) {\n  const { isMobile, openMobile, state, toggleSidebar } = useSidebar();\n  const sidebarOpen = isMobile ? openMobile : state === 'expanded';\n  const [customRange, setCustomRange] = useState<DateRange | null>(null);\n  const [unread, setUnread] = useState(true);\n  const [notificationsOpen, setNotificationsOpen] = useState(false);\n  const from = parseISO(range.from);\n  const to = parseISO(range.to);\n  const rangeLabel =\n    range.from === range.to\n      ? format(to, 'd MMM')\n      : `${format(from, from.getMonth() === to.getMonth() && from.getFullYear() === to.getFullYear() ? 'd' : 'd MMM')} – ${format(to, 'd MMM')}`;\n\n  return (\n    <>\n      <header className=\"flex min-h-15 flex-wrap items-center justify-between gap-3 border-b px-4 py-3 lg:px-7 lg:py-2\">\n        <div className=\"flex items-center gap-3\">\n          <Button\n            aria-expanded={sidebarOpen}\n            aria-label={sidebarOpen ? 'Collapse sidebar' : 'Expand sidebar'}\n            onClick={toggleSidebar}\n            size=\"icon-sm\"\n            type=\"button\"\n            variant=\"ghost\"\n          >\n            {sidebarOpen ? (\n              <PanelLeftClose aria-hidden=\"true\" />\n            ) : (\n              <PanelLeftOpen aria-hidden=\"true\" />\n            )}\n          </Button>\n          <h1 className=\"font-semibold text-lg tracking-tight\">Overview</h1>\n          <p className=\"text-[0.8125rem] text-muted-foreground max-sm:hidden\">\n            Updated 2 min ago\n          </p>\n        </div>\n        <div className=\"flex flex-wrap items-center gap-2 sm:gap-3\">\n          <ToggleGroup\n            aria-label=\"Chart period\"\n            className=\"rounded-[9px] border bg-muted/50 p-0.75\"\n            onValueChange={(values) => {\n              const next = periods.find((item) => item.value === values[0]);\n              if (!next) {\n                return;\n              }\n              onPeriodChange(next.value);\n              onRangeChange({ from: next.from, to: '2025-11-16' });\n            }}\n            size=\"sm\"\n            spacing={0.5}\n            value={[period]}\n          >\n            {periods.map((item) => (\n              <ToggleGroupItem\n                className=\"h-7 min-w-0 rounded-md px-3 text-[0.8125rem]\"\n                key={item.value}\n                value={item.value}\n              >\n                {item.label}\n              </ToggleGroupItem>\n            ))}\n          </ToggleGroup>\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              render={\n                <Button\n                  aria-label={`Date range: ${rangeLabel}`}\n                  className={press}\n                  size=\"lg\"\n                  variant=\"outline\"\n                />\n              }\n            >\n              <CalendarDays data-icon=\"inline-start\" />\n              {rangeLabel}\n            </DropdownMenuTrigger>\n            <DropdownMenuContent\n              align=\"end\"\n              className=\"w-50 rounded-[10px] p-1.5\"\n            >\n              <DropdownMenuGroup>\n                <DropdownMenuLabel className=\"sr-only\">\n                  Date range\n                </DropdownMenuLabel>\n                <DropdownMenuRadioGroup\n                  onValueChange={(value) => {\n                    const next = ranges.find((item) => item.from === value);\n                    if (next) {\n                      onRangeChange({ from: next.from, to: '2025-11-16' });\n                    }\n                  }}\n                  value={range.to === '2025-11-16' ? range.from : ''}\n                >\n                  {ranges.map((item) => (\n                    <DropdownMenuRadioItem\n                      className=\"min-h-8.5 px-2.5\"\n                      key={item.label}\n                      value={item.from}\n                    >\n                      {item.label}\n                    </DropdownMenuRadioItem>\n                  ))}\n                </DropdownMenuRadioGroup>\n              </DropdownMenuGroup>\n              <DropdownMenuSeparator />\n              <DropdownMenuGroup>\n                <DropdownMenuItem\n                  className=\"min-h-8.5 px-2.5\"\n                  onClick={() => setCustomRange(range)}\n                >\n                  Custom range…\n                </DropdownMenuItem>\n              </DropdownMenuGroup>\n            </DropdownMenuContent>\n          </DropdownMenu>\n          <Separator className=\"h-5! max-sm:hidden\" orientation=\"vertical\" />\n          <Button\n            aria-label=\"Toggle dark mode\"\n            className={cn(press, 'relative')}\n            onClick={() => document.documentElement.classList.toggle('dark')}\n            size=\"icon-lg\"\n            title=\"Toggle dark mode (Shift+D)\"\n            type=\"button\"\n            variant=\"ghost\"\n          >\n            <Sun className=\"scale-100 opacity-100 transition-[opacity,scale] duration-150 ease-out dark:scale-50 dark:opacity-0\" />\n            <Moon className=\"absolute scale-50 opacity-0 transition-[opacity,scale] duration-150 ease-out dark:scale-100 dark:opacity-100\" />\n          </Button>\n          <Popover onOpenChange={setNotificationsOpen} open={notificationsOpen}>\n            <PopoverTrigger\n              render={\n                <Button\n                  aria-label={\n                    unread ? 'Notifications, unread updates' : 'Notifications'\n                  }\n                  className={cn(press, 'relative')}\n                  size=\"icon-lg\"\n                  variant=\"ghost\"\n                />\n              }\n            >\n              <Bell />\n              {unread && (\n                <span className=\"absolute top-2 right-2 size-1.5 rounded-full bg-blue-500 ring-2 ring-background\" />\n              )}\n            </PopoverTrigger>\n            <PopoverContent\n              align=\"end\"\n              className=\"w-90 max-w-[calc(100vw-2rem)] gap-0 overflow-hidden rounded-xl p-0\"\n              sideOffset={8}\n            >\n              <div className=\"flex h-13 items-center justify-between gap-3 border-b px-4\">\n                <PopoverTitle>Notifications</PopoverTitle>\n                <div className=\"text-muted-foreground text-xs tabular-nums\">\n                  {unread ? '2 unread' : 'All read'}\n                </div>\n                <PopoverDescription className=\"sr-only\">\n                  Recent workspace activity.\n                </PopoverDescription>\n              </div>\n              <Button\n                className=\"h-auto w-full items-start justify-start gap-3 whitespace-normal rounded-none p-4 text-left font-normal\"\n                onClick={() => setNotificationsOpen(false)}\n                render={\n                  <a href=\"#revenue\">\n                    <div className=\"flex size-9 shrink-0 items-center justify-center rounded-lg border bg-background\">\n                      <ChartNoAxesCombined\n                        aria-hidden=\"true\"\n                        className=\"size-4 text-muted-foreground\"\n                      />\n                    </div>\n                    <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n                      <div className=\"flex items-center justify-between gap-3 text-sm\">\n                        <span className=\"font-medium\">Weekly report</span>\n                        <time className=\"shrink-0 text-muted-foreground text-xs\">\n                          2m ago\n                        </time>\n                      </div>\n                      <p className=\"text-[0.8125rem] text-muted-foreground leading-5\">\n                        10–16 Nov · $42,910 in revenue\n                      </p>\n                      <p className=\"text-muted-foreground text-xs\">\n                        3,482 charges processed\n                      </p>\n                    </div>\n                    <span\n                      aria-hidden=\"true\"\n                      className={\n                        unread\n                          ? 'mt-1.5 size-1.5 shrink-0 rounded-full bg-blue-500'\n                          : 'mt-1.5 size-1.5 shrink-0'\n                      }\n                    />\n                  </a>\n                }\n                variant=\"ghost\"\n              />\n              <Separator />\n              <Button\n                className=\"h-auto w-full items-start justify-start gap-3 whitespace-normal rounded-none p-4 text-left font-normal\"\n                onClick={() => setNotificationsOpen(false)}\n                render={\n                  <a href=\"#customers\">\n                    <Avatar className=\"size-9 rounded-lg after:rounded-lg\">\n                      <AvatarImage\n                        alt=\"\"\n                        className=\"rounded-lg\"\n                        src=\"https://i.pravatar.cc/96?img=47\"\n                      />\n                      <AvatarFallback className=\"rounded-lg\">PR</AvatarFallback>\n                    </Avatar>\n                    <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n                      <div className=\"flex items-center justify-between gap-3 text-sm\">\n                        <span className=\"font-medium\">\n                          Priya Raman signed up\n                        </span>\n                        <time className=\"shrink-0 text-muted-foreground text-xs\">\n                          18m ago\n                        </time>\n                      </div>\n                      <p className=\"text-[0.8125rem] text-muted-foreground leading-5\">\n                        Joined Lumen Labs on the Pro plan.\n                      </p>\n                    </div>\n                    <span\n                      aria-hidden=\"true\"\n                      className={\n                        unread\n                          ? 'mt-1.5 size-1.5 shrink-0 rounded-full bg-blue-500'\n                          : 'mt-1.5 size-1.5 shrink-0'\n                      }\n                    />\n                  </a>\n                }\n                variant=\"ghost\"\n              />\n              <div className=\"flex items-center justify-end border-t px-3 py-2\">\n                <Button\n                  disabled={!unread}\n                  onClick={() => setUnread(false)}\n                  size=\"sm\"\n                  type=\"button\"\n                  variant=\"ghost\"\n                >\n                  Mark all as read\n                </Button>\n              </div>\n            </PopoverContent>\n          </Popover>\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              render={\n                <Button\n                  aria-label=\"Account menu\"\n                  className={cn(press, 'gap-2 rounded-full pr-2.5 pl-1')}\n                  size=\"lg\"\n                  variant=\"outline\"\n                />\n              }\n            >\n              <Avatar className=\"size-7\">\n                <AvatarFallback className=\"bg-muted font-semibold text-[0.6875rem] text-foreground\">\n                  EB\n                </AvatarFallback>\n              </Avatar>\n              <ChevronDown data-icon=\"inline-end\" />\n            </DropdownMenuTrigger>\n            <DropdownMenuContent\n              align=\"end\"\n              className=\"w-60 rounded-[10px] p-1.5\"\n            >\n              <DropdownMenuGroup>\n                <DropdownMenuLabel className=\"flex flex-col gap-0.5 px-2.5 py-2\">\n                  <div className=\"font-medium text-foreground text-sm\">\n                    Ephraim Brooks\n                  </div>\n                  <div className=\"font-normal text-[0.8125rem]\">\n                    ephraim@lumenlabs.io\n                  </div>\n                </DropdownMenuLabel>\n              </DropdownMenuGroup>\n              <DropdownMenuSeparator />\n              <DropdownMenuGroup>\n                <DropdownMenuItem\n                  className=\"min-h-8.5 gap-2.5 px-2.5\"\n                  render={\n                    <a aria-label=\"Profile\" href=\"/settings/profile\">\n                      <UserRound />\n                      Profile\n                    </a>\n                  }\n                />\n                <DropdownMenuItem\n                  className=\"min-h-8.5 gap-2.5 px-2.5\"\n                  render={\n                    <a\n                      aria-label=\"Notifications\"\n                      href=\"/settings/notifications\"\n                    >\n                      <Bell />\n                      Notifications\n                    </a>\n                  }\n                />\n                <DropdownMenuItem\n                  className=\"min-h-8.5 gap-2.5 px-2.5\"\n                  render={\n                    <a aria-label=\"Security\" href=\"/settings/security\">\n                      <LockKeyhole />\n                      Security\n                    </a>\n                  }\n                />\n              </DropdownMenuGroup>\n              <DropdownMenuSeparator />\n              <DropdownMenuGroup>\n                <DropdownMenuItem\n                  className=\"min-h-8.5 gap-2.5 px-2.5\"\n                  render={\n                    <a aria-label=\"Log out\" href=\"/login\">\n                      <LogOut />\n                      Log out\n                    </a>\n                  }\n                  variant=\"destructive\"\n                />\n              </DropdownMenuGroup>\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </div>\n      </header>\n      <Dialog\n        onOpenChange={(open) => {\n          if (!open) {\n            setCustomRange(null);\n          }\n        }}\n        open={customRange !== null}\n      >\n        <DialogContent>\n          {customRange && (\n            <form\n              className=\"flex flex-col gap-5\"\n              onSubmit={(event) => {\n                event.preventDefault();\n                if (\n                  customRange.from &&\n                  customRange.to &&\n                  customRange.from <= customRange.to\n                ) {\n                  onRangeChange(customRange);\n                  setCustomRange(null);\n                }\n              }}\n            >\n              <DialogHeader>\n                <DialogTitle>Custom date range</DialogTitle>\n                <DialogDescription>\n                  Choose the dates to include in your charts.\n                </DialogDescription>\n              </DialogHeader>\n              <FieldGroup>\n                <Field>\n                  <FieldLabel htmlFor=\"dashboard-from\">Start date</FieldLabel>\n                  <Input\n                    id=\"dashboard-from\"\n                    max={customRange.to}\n                    name=\"from\"\n                    onChange={(event) =>\n                      setCustomRange({\n                        ...customRange,\n                        from: event.target.value,\n                      })\n                    }\n                    required\n                    type=\"date\"\n                    value={customRange.from}\n                  />\n                </Field>\n                <Field>\n                  <FieldLabel htmlFor=\"dashboard-to\">End date</FieldLabel>\n                  <Input\n                    id=\"dashboard-to\"\n                    min={customRange.from}\n                    name=\"to\"\n                    onChange={(event) =>\n                      setCustomRange({ ...customRange, to: event.target.value })\n                    }\n                    required\n                    type=\"date\"\n                    value={customRange.to}\n                  />\n                </Field>\n              </FieldGroup>\n              <DialogFooter>\n                <Button\n                  onClick={() => setCustomRange(null)}\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  Cancel\n                </Button>\n                <Button className={press} type=\"submit\">\n                  Apply range\n                </Button>\n              </DialogFooter>\n            </form>\n          )}\n        </DialogContent>\n      </Dialog>\n    </>\n  );\n}\n\nconst periods = [\n  { label: 'Day', value: 'day', from: '2025-11-16' },\n  { label: 'Week', value: 'week', from: '2025-11-10' },\n  { label: 'Month', value: 'month', from: '2025-10-18' },\n  { label: 'Year', value: 'year', from: '2025-01-01' },\n] as const;\n\nconst ranges = [\n  { label: 'Last 7 days', from: '2025-11-10' },\n  { label: 'Last 14 days', from: '2025-11-03' },\n  { label: 'Last 30 days', from: '2025-10-18' },\n  { label: 'This quarter', from: '2025-10-01' },\n  { label: 'Year to date', from: '2025-01-01' },\n];\n"
        },
        {
          "path": "content/components/dashboard/dashboard-01/dashboard-sidebar.tsx",
          "type": "registry:component",
          "target": "components/dashboard-01/dashboard-sidebar.tsx",
          "content": "'use client';\n\nimport {\n  ArrowLeftRight,\n  BriefcaseBusiness,\n  ChartNoAxesCombined,\n  Check,\n  ChevronDown,\n  CircleHelp,\n  CirclePlus,\n  CreditCard,\n  LayoutDashboard,\n  Moon,\n  Settings,\n  Users,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Field, FieldGroup, FieldLabel } from '@/components/ui/field';\nimport { Input } from '@/components/ui/input';\nimport { Progress } from '@/components/ui/progress';\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\n\nexport function DashboardSidebar() {\n  const { isMobile, setOpenMobile, state } = useSidebar();\n  const [workspaces, setWorkspaces] = useState([\n    'Lumen Labs',\n    'Halcyon Studio',\n    'Pinecrest Co.',\n  ]);\n  const [workspace, setWorkspace] = useState('Lumen Labs');\n  const [creating, setCreating] = useState(false);\n  const [name, setName] = useState('');\n\n  return (\n    <>\n      <Sidebar collapsible=\"icon\">\n        <SidebarHeader className=\"px-3 pt-3.5 pb-5\">\n          <SidebarMenu>\n            <SidebarMenuItem>\n              <DropdownMenu>\n                <DropdownMenuTrigger\n                  render={\n                    <SidebarMenuButton\n                      aria-label={`${workspace}, switch workspace`}\n                      className=\"h-9 gap-2 rounded-lg px-2 font-medium group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0!\"\n                    />\n                  }\n                >\n                  <span className=\"flex size-5 shrink-0 items-center justify-center rounded-[5px] bg-blue-500 font-semibold text-[0.6875rem] text-white\">\n                    {workspace.slice(0, 1)}\n                  </span>\n                  <span className=\"flex-1 truncate group-data-[collapsible=icon]:hidden\">\n                    {workspace}\n                  </span>\n                  <ChevronDown className=\"text-muted-foreground group-data-[collapsible=icon]:hidden\" />\n                </DropdownMenuTrigger>\n                <DropdownMenuContent\n                  className={cn(\n                    'rounded-[10px] p-1.5',\n                    state === 'collapsed' && !isMobile\n                      ? 'w-56'\n                      : 'w-(--anchor-width)'\n                  )}\n                  side={state === 'collapsed' && !isMobile ? 'right' : 'bottom'}\n                  sideOffset={8}\n                >\n                  <DropdownMenuGroup>\n                    <DropdownMenuLabel className=\"px-2.5 py-2\">\n                      Workspaces\n                    </DropdownMenuLabel>\n                    {workspaces.map((item, index) => (\n                      <DropdownMenuItem\n                        className={cn(\n                          'min-h-9 gap-2.5 px-2.5',\n                          item === workspace && 'bg-accent'\n                        )}\n                        key={item}\n                        onClick={() => setWorkspace(item)}\n                      >\n                        <span\n                          className={cn(\n                            'flex size-5 shrink-0 items-center justify-center rounded-[5px] font-semibold text-[0.6875rem]',\n                            index === 0\n                              ? 'bg-blue-500 text-white'\n                              : 'bg-muted text-muted-foreground'\n                          )}\n                        >\n                          {item.slice(0, 1)}\n                        </span>\n                        <span className=\"flex-1 truncate\">{item}</span>\n                        {item === workspace && <Check aria-label=\"Selected\" />}\n                      </DropdownMenuItem>\n                    ))}\n                  </DropdownMenuGroup>\n                  <DropdownMenuSeparator />\n                  <DropdownMenuGroup>\n                    <DropdownMenuItem\n                      className=\"min-h-9 gap-2.5 px-2.5\"\n                      onClick={() => setCreating(true)}\n                    >\n                      <CirclePlus />\n                      Create workspace\n                    </DropdownMenuItem>\n                  </DropdownMenuGroup>\n                </DropdownMenuContent>\n              </DropdownMenu>\n            </SidebarMenuItem>\n          </SidebarMenu>\n        </SidebarHeader>\n        <SidebarContent>\n          <SidebarGroup className=\"px-3 py-0\">\n            <SidebarGroupLabel className=\"px-2.5 text-[0.6875rem] tracking-wider\">\n              WORKSPACE\n            </SidebarGroupLabel>\n            <SidebarMenu className=\"gap-0.5\">\n              {navigation.map((item) => (\n                <SidebarMenuItem\n                  className={\n                    item.label === 'Overview'\n                      ? 'before:-left-3 before:absolute before:top-2.5 before:h-4 before:w-0.75 before:rounded-r-sm before:bg-blue-500'\n                      : undefined\n                  }\n                  key={item.label}\n                >\n                  <SidebarMenuButton\n                    className=\"h-9 gap-2.5 rounded-lg px-2.5\"\n                    isActive={item.label === 'Overview'}\n                    onClick={() => setOpenMobile(false)}\n                    render={\n                      <a aria-label={item.label} href={item.href}>\n                        <item.icon\n                          aria-hidden=\"true\"\n                          className={\n                            item.label === 'Overview'\n                              ? undefined\n                              : 'text-muted-foreground'\n                          }\n                        />\n                        <span className=\"flex-1 group-data-[collapsible=icon]:hidden\">\n                          {item.label}\n                        </span>\n                        {item.label === 'Customers' && (\n                          <div className=\"rounded-full bg-muted px-1.5 text-[0.6875rem] text-muted-foreground tabular-nums group-data-[collapsible=icon]:hidden\">\n                            12\n                          </div>\n                        )}\n                      </a>\n                    }\n                    tooltip={item.label}\n                  />\n                </SidebarMenuItem>\n              ))}\n            </SidebarMenu>\n          </SidebarGroup>\n        </SidebarContent>\n        <SidebarFooter className=\"gap-3 px-3 pb-5\">\n          <SidebarMenu className=\"gap-0.5\">\n            <SidebarMenuItem>\n              <DropdownMenu>\n                <DropdownMenuTrigger\n                  render={\n                    <SidebarMenuButton\n                      aria-label=\"Settings\"\n                      className=\"h-9 gap-2.5 rounded-lg px-2.5\"\n                      tooltip=\"Settings\"\n                    />\n                  }\n                >\n                  <Settings className=\"text-muted-foreground\" />\n                  <span className=\"group-data-[collapsible=icon]:hidden\">\n                    Settings\n                  </span>\n                </DropdownMenuTrigger>\n                <DropdownMenuContent\n                  className=\"w-64 rounded-[10px] p-1.5\"\n                  side=\"top\"\n                  sideOffset={8}\n                >\n                  <DropdownMenuGroup>\n                    <DropdownMenuItem\n                      className=\"min-h-9 gap-2.5 px-2.5\"\n                      render={\n                        <a\n                          aria-label=\"Company profile\"\n                          href=\"/settings/company\"\n                        >\n                          <BriefcaseBusiness />\n                          Company profile\n                        </a>\n                      }\n                    />\n                    <DropdownMenuItem\n                      className=\"min-h-9 gap-2.5 px-2.5\"\n                      render={\n                        <a aria-label=\"Team\" href=\"/settings/team\">\n                          <Users />\n                          Team\n                        </a>\n                      }\n                    />\n                    <DropdownMenuItem\n                      className=\"min-h-9 gap-2.5 px-2.5\"\n                      render={\n                        <a aria-label=\"Billing\" href=\"/settings/billing\">\n                          <CreditCard />\n                          Billing\n                        </a>\n                      }\n                    />\n                    <DropdownMenuItem\n                      className=\"min-h-9 gap-2.5 px-2.5\"\n                      closeOnClick={false}\n                      onClick={() =>\n                        document.documentElement.classList.toggle('dark')\n                      }\n                    >\n                      <Moon />\n                      <span className=\"flex-1\">Appearance</span>\n                      <div className=\"text-muted-foreground text-xs\">\n                        <span className=\"dark:hidden\">Light</span>\n                        <span className=\"not-dark:hidden\">Dark</span>\n                      </div>\n                    </DropdownMenuItem>\n                  </DropdownMenuGroup>\n                </DropdownMenuContent>\n              </DropdownMenu>\n            </SidebarMenuItem>\n            <SidebarMenuItem>\n              <SidebarMenuButton\n                className=\"h-9 gap-2.5 rounded-lg px-2.5\"\n                render={\n                  <a\n                    aria-label=\"Help and support\"\n                    href=\"mailto:support@lumenlabs.io\"\n                  >\n                    <CircleHelp className=\"text-muted-foreground\" />\n                    <span className=\"group-data-[collapsible=icon]:hidden\">\n                      Help &amp; support\n                    </span>\n                  </a>\n                }\n                tooltip=\"Help and support\"\n              />\n            </SidebarMenuItem>\n          </SidebarMenu>\n          <div className=\"flex flex-col gap-3 rounded-[10px] border border-border bg-background p-3 group-data-[collapsible=icon]:hidden dark:border-input dark:bg-input/30\">\n            <div className=\"flex items-center justify-between text-[0.8125rem]\">\n              <span className=\"font-medium\">Monthly volume</span>\n              <div className=\"text-[0.8125rem] text-muted-foreground tabular-nums\">\n                72%\n              </div>\n            </div>\n            <Progress\n              aria-label=\"Monthly volume used\"\n              className=\"[&_[data-slot=progress-indicator]]:bg-blue-500 [&_[data-slot=progress-track]]:bg-border\"\n              value={72.4}\n            />\n            <p className=\"text-muted-foreground text-xs tabular-nums\">\n              $72,400 of $100,000 used\n            </p>\n          </div>\n        </SidebarFooter>\n      </Sidebar>\n      <Dialog onOpenChange={setCreating} open={creating}>\n        <DialogContent>\n          <form\n            className=\"flex flex-col gap-5\"\n            onSubmit={(event) => {\n              event.preventDefault();\n              const nextName = name.trim();\n              if (!nextName) {\n                return;\n              }\n              if (!workspaces.includes(nextName)) {\n                setWorkspaces([...workspaces, nextName]);\n              }\n              setWorkspace(nextName);\n              setName('');\n              setCreating(false);\n            }}\n          >\n            <DialogHeader>\n              <DialogTitle>Create workspace</DialogTitle>\n              <DialogDescription>\n                Add a workspace to this dashboard preview.\n              </DialogDescription>\n            </DialogHeader>\n            <FieldGroup>\n              <Field>\n                <FieldLabel htmlFor=\"workspace-name\">Workspace name</FieldLabel>\n                <Input\n                  autoComplete=\"organization\"\n                  id=\"workspace-name\"\n                  maxLength={60}\n                  name=\"workspace-name\"\n                  onChange={(event) => setName(event.target.value)}\n                  pattern=\".*\\S.*\"\n                  required\n                  value={name}\n                />\n              </Field>\n            </FieldGroup>\n            <DialogFooter>\n              <Button\n                onClick={() => setCreating(false)}\n                type=\"button\"\n                variant=\"outline\"\n              >\n                Cancel\n              </Button>\n              <Button type=\"submit\">Create workspace</Button>\n            </DialogFooter>\n          </form>\n        </DialogContent>\n      </Dialog>\n    </>\n  );\n}\n\nconst navigation = [\n  { label: 'Overview', href: '#overview', icon: LayoutDashboard },\n  { label: 'Customers', href: '#customers', icon: Users },\n  { label: 'Transactions', href: '/transactions', icon: ArrowLeftRight },\n  { label: 'Payouts', href: '/payouts', icon: CreditCard },\n  { label: 'Reports', href: '#revenue', icon: ChartNoAxesCombined },\n];\n"
        },
        {
          "path": "content/components/dashboard/dashboard-01/customers-table.tsx",
          "type": "registry:component",
          "target": "components/dashboard-01/customers-table.tsx",
          "content": "'use client';\n\nimport {\n  DownloadIcon,\n  EllipsisIcon,\n  EyeIcon,\n  FunnelIcon,\n  MailIcon,\n  SearchIcon,\n  XIcon,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog';\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n  InputGroupText,\n} from '@/components/ui/input-group';\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '@/components/ui/table';\nimport { cn } from '@/lib/utils';\n\nconst easeOut = 'ease-[cubic-bezier(0.23,1,0.32,1)]';\nconst press = `${easeOut} transition-[scale,background-color] duration-150 active:scale-[0.96] motion-reduce:active:scale-100`;\nconst swap = `${easeOut} fade-in zoom-in-95 motion-reduce:zoom-in-100 animate-in duration-150`;\n\nexport function CustomersTable() {\n  const [query, setQuery] = useState('');\n  const [selected, setSelected] = useState<Record<Facet, readonly string[]>>({\n    Plan: [],\n    Status: [],\n  });\n  const [page, setPage] = useState(1);\n  const [customer, setCustomer] = useState<Customer | null>(null);\n\n  const bindSearch = (node: HTMLInputElement | null) => {\n    if (!node) {\n      return;\n    }\n\n    const focusSearch = (event: KeyboardEvent) => {\n      if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {\n        event.preventDefault();\n        node.focus();\n      }\n    };\n    window.addEventListener('keydown', focusSearch);\n    return () => window.removeEventListener('keydown', focusSearch);\n  };\n\n  const term = query.trim().toLocaleLowerCase();\n  const filtered = customers.filter(\n    (item) =>\n      (!term ||\n        item.name.toLocaleLowerCase().includes(term) ||\n        item.email.toLocaleLowerCase().includes(term)) &&\n      (!selected.Plan.length || selected.Plan.includes(item.plan)) &&\n      (!selected.Status.length || selected.Status.includes(item.status))\n  );\n\n  const pageCount = Math.max(1, Math.ceil(filtered.length / 9));\n  const visibleCustomers = filtered.slice((page - 1) * 9, page * 9);\n  const filterCount = selected.Plan.length + selected.Status.length;\n  const toggle = (facet: Facet, option: string) => {\n    setSelected((current) => ({\n      ...current,\n      [facet]: current[facet].includes(option)\n        ? current[facet].filter((item) => item !== option)\n        : [...current[facet], option],\n    }));\n    setPage(1);\n  };\n  const clear = (facet?: Facet) => {\n    setSelected((current) =>\n      facet ? { ...current, [facet]: [] } : { Plan: [], Status: [] }\n    );\n    setPage(1);\n  };\n\n  const exportCustomers = () => {\n    const csv = [\n      ['Customer', 'Email', 'Signed up', 'Plan', 'Status'],\n      ...filtered.map((item) => [\n        item.name,\n        item.email,\n        item.signedUp,\n        item.plan,\n        item.status,\n      ]),\n    ]\n      .map((row) =>\n        row.map((value) => `\"${value.replaceAll('\"', '\"\"')}\"`).join(',')\n      )\n      .join('\\n');\n    const url = URL.createObjectURL(\n      new Blob([csv], { type: 'text/csv;charset=utf-8' })\n    );\n    const link = document.createElement('a');\n    link.href = url;\n    link.download = 'customers.csv';\n    link.click();\n    URL.revokeObjectURL(url);\n  };\n\n  return (\n    <section className=\"@container/customers w-full\" id=\"customers\">\n      <div className=\"flex @3xl/customers:flex-row flex-col @3xl/customers:items-center @3xl/customers:justify-between gap-4 p-4 sm:p-7 sm:py-6\">\n        <div>\n          <h2 className=\"font-semibold text-[18px] leading-6\">New customers</h2>\n          <p className=\"mt-1 text-[13px] text-muted-foreground\">\n            Recent signups across all plans\n          </p>\n        </div>\n        <div className=\"flex min-w-0 flex-1 flex-wrap items-center @3xl/customers:justify-end gap-2\">\n          {filterCount > 0 && (\n            <fieldset\n              aria-label=\"Active customer filters\"\n              className=\"flex flex-wrap items-center gap-2\"\n            >\n              {facets.map(\n                (facet) =>\n                  selected[facet].length > 0 && (\n                    <div\n                      className={cn(\n                        swap,\n                        'flex h-7 items-center rounded-md bg-muted pl-2 text-[0.8125rem]'\n                      )}\n                      key={facet}\n                    >\n                      <DropdownMenu>\n                        <DropdownMenuTrigger\n                          render={\n                            <button\n                              className=\"flex h-full max-w-56 items-center gap-1 rounded-l-md outline-none focus-visible:ring-3 focus-visible:ring-ring/50\"\n                              type=\"button\"\n                            />\n                          }\n                        >\n                          <span className=\"text-muted-foreground\">\n                            {facet}:\n                          </span>\n                          <span className=\"truncate font-medium\">\n                            {selected[facet].join(', ')}\n                          </span>\n                        </DropdownMenuTrigger>\n                        <DropdownMenuContent className=\"w-44 rounded-[10px] p-1.5\">\n                          <DropdownMenuGroup>\n                            {options[facet].map((option) => (\n                              <DropdownMenuCheckboxItem\n                                checked={selected[facet].includes(option)}\n                                className=\"min-h-9 px-2.5\"\n                                closeOnClick={false}\n                                key={option}\n                                onCheckedChange={() => toggle(facet, option)}\n                              >\n                                {option}\n                              </DropdownMenuCheckboxItem>\n                            ))}\n                          </DropdownMenuGroup>\n                        </DropdownMenuContent>\n                      </DropdownMenu>\n                      <button\n                        aria-label={`Remove ${facet.toLowerCase()} filter`}\n                        className=\"flex h-full w-6 items-center justify-center rounded-r-md text-muted-foreground outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50\"\n                        onClick={() => clear(facet)}\n                        type=\"button\"\n                      >\n                        <XIcon aria-hidden=\"true\" className=\"size-3.5\" />\n                      </button>\n                    </div>\n                  )\n              )}\n              <Button\n                onClick={() => clear()}\n                size=\"sm\"\n                type=\"button\"\n                variant=\"ghost\"\n              >\n                Clear all\n              </Button>\n            </fieldset>\n          )}\n          <label className=\"sr-only\" htmlFor=\"customer-search\">\n            Search customers\n          </label>\n          <InputGroup className=\"h-9 @3xl/customers:w-60 w-full border-border bg-background dark:border-input\">\n            <InputGroupAddon>\n              <InputGroupText>\n                <SearchIcon aria-hidden=\"true\" />\n              </InputGroupText>\n            </InputGroupAddon>\n            <InputGroupInput\n              className=\"text-[16px] sm:text-sm\"\n              id=\"customer-search\"\n              name=\"customer-search\"\n              onChange={(event) => {\n                setQuery(event.target.value);\n                setPage(1);\n              }}\n              placeholder=\"Search customers\"\n              ref={bindSearch}\n              type=\"search\"\n              value={query}\n            />\n            <InputGroupAddon align=\"inline-end\">\n              <kbd className=\"hidden rounded border bg-muted px-1.5 py-0.5 font-medium text-[10px] sm:block\">\n                ⌘K\n              </kbd>\n            </InputGroupAddon>\n          </InputGroup>\n          <div className=\"flex gap-2\">\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <Button\n                    className={cn(press, 'h-9 flex-1 sm:flex-none')}\n                    size=\"lg\"\n                    variant=\"outline\"\n                  />\n                }\n              >\n                <FunnelIcon aria-hidden=\"true\" />\n                Filter{filterCount ? ` (${filterCount})` : ''}\n              </DropdownMenuTrigger>\n              <DropdownMenuContent align=\"end\" className=\"w-48\">\n                {facets.map((facet, index) => (\n                  <DropdownMenuGroup key={facet}>\n                    {index > 0 && <DropdownMenuSeparator />}\n                    <DropdownMenuLabel>{facet}</DropdownMenuLabel>\n                    {options[facet].map((option) => (\n                      <DropdownMenuCheckboxItem\n                        checked={selected[facet].includes(option)}\n                        key={option}\n                        onCheckedChange={() => toggle(facet, option)}\n                      >\n                        {option}\n                      </DropdownMenuCheckboxItem>\n                    ))}\n                  </DropdownMenuGroup>\n                ))}\n                {filterCount > 0 && (\n                  <>\n                    <DropdownMenuSeparator />\n                    <DropdownMenuGroup>\n                      <DropdownMenuItem onClick={() => clear()}>\n                        Clear filters\n                      </DropdownMenuItem>\n                    </DropdownMenuGroup>\n                  </>\n                )}\n              </DropdownMenuContent>\n            </DropdownMenu>\n            <Button\n              className={cn(press, 'h-9 flex-1 sm:flex-none')}\n              onClick={exportCustomers}\n              size=\"lg\"\n              variant=\"outline\"\n            >\n              <DownloadIcon aria-hidden=\"true\" />\n              Export\n            </Button>\n          </div>\n        </div>\n      </div>\n\n      <div className=\"border-t\">\n        <Table className=\"min-w-245 table-fixed [&_td:first-child]:pl-4 sm:[&_td:first-child]:pl-7 [&_td:last-child]:pr-4 sm:[&_td:last-child]:pr-7 [&_th:first-child]:pl-4 sm:[&_th:first-child]:pl-7 [&_th:last-child]:pr-4 sm:[&_th:last-child]:pr-7\">\n          <colgroup>\n            <col className=\"w-[27%]\" />\n            <col className=\"w-[23%]\" />\n            <col className=\"w-[18%]\" />\n            <col className=\"w-[12%]\" />\n            <col className=\"w-[calc(20%-68px)]\" />\n            <col className=\"w-17\" />\n          </colgroup>\n          <TableHeader>\n            <TableRow className=\"h-11 hover:bg-transparent\">\n              <TableHead className=\"px-2 text-muted-foreground text-xs\">\n                Customer\n              </TableHead>\n              <TableHead className=\"px-2 text-muted-foreground text-xs\">\n                Email\n              </TableHead>\n              <TableHead className=\"px-2 text-muted-foreground text-xs\">\n                Signed up\n              </TableHead>\n              <TableHead className=\"px-2 text-muted-foreground text-xs\">\n                Plan\n              </TableHead>\n              <TableHead className=\"px-2 text-muted-foreground text-xs\">\n                Status\n              </TableHead>\n              <TableHead>\n                <span className=\"sr-only\">Actions</span>\n              </TableHead>\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            {visibleCustomers.map((item) => (\n              <TableRow className=\"h-14\" key={item.email}>\n                <TableCell className=\"px-2 py-0 font-medium text-sm\">\n                  {item.name}\n                </TableCell>\n                <TableCell className=\"px-2 py-0 text-muted-foreground text-sm\">\n                  {item.email}\n                </TableCell>\n                <TableCell className=\"px-2 py-0 text-muted-foreground text-sm\">\n                  {item.signedUp}\n                </TableCell>\n                <TableCell className=\"px-2 py-0 text-sm\">{item.plan}</TableCell>\n                <TableCell className=\"px-2 py-0\">\n                  <StatusDot status={item.status} />\n                </TableCell>\n                <TableCell className=\"px-0 py-0 text-right\">\n                  <DropdownMenu>\n                    <DropdownMenuTrigger\n                      render={\n                        <Button\n                          aria-label={`Actions for ${item.name}`}\n                          size=\"icon-sm\"\n                          variant=\"ghost\"\n                        />\n                      }\n                    >\n                      <EllipsisIcon aria-hidden=\"true\" />\n                    </DropdownMenuTrigger>\n                    <DropdownMenuContent\n                      align=\"end\"\n                      className=\"w-60 rounded-[10px] p-1.5\"\n                      sideOffset={6}\n                    >\n                      <DropdownMenuGroup>\n                        <DropdownMenuLabel className=\"flex flex-col gap-1 px-2.5 py-2\">\n                          <div className=\"truncate font-medium text-foreground text-sm\">\n                            {item.name}\n                          </div>\n                          <div className=\"truncate font-normal text-xs\">\n                            {item.email}\n                          </div>\n                        </DropdownMenuLabel>\n                      </DropdownMenuGroup>\n                      <DropdownMenuSeparator />\n                      <DropdownMenuGroup>\n                        <DropdownMenuItem\n                          className=\"min-h-9 gap-2.5 px-2.5\"\n                          onClick={() => setCustomer(item)}\n                        >\n                          <EyeIcon\n                            aria-hidden=\"true\"\n                            className=\"text-muted-foreground\"\n                          />\n                          View customer\n                        </DropdownMenuItem>\n                        <DropdownMenuItem\n                          className=\"min-h-9 gap-2.5 px-2.5\"\n                          render={\n                            <a\n                              aria-label={`Send email to ${item.name}`}\n                              href={`mailto:${item.email}`}\n                            >\n                              <MailIcon\n                                aria-hidden=\"true\"\n                                className=\"text-muted-foreground\"\n                              />\n                              Send email\n                            </a>\n                          }\n                        />\n                      </DropdownMenuGroup>\n                    </DropdownMenuContent>\n                  </DropdownMenu>\n                </TableCell>\n              </TableRow>\n            ))}\n            {!visibleCustomers.length && (\n              <TableRow className=\"h-28 hover:bg-transparent\">\n                <TableCell\n                  className=\"text-center text-muted-foreground text-sm\"\n                  colSpan={6}\n                >\n                  No customers match your search and filters.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n\n      <div className=\"flex flex-col gap-3 border-t p-4 sm:flex-row sm:items-center sm:justify-between sm:px-7\">\n        <p aria-live=\"polite\" className=\"text-[13px] text-muted-foreground\">\n          {filtered.length\n            ? `${(page - 1) * 9 + 1}–${Math.min(page * 9, filtered.length)} of ${filtered.length} customers`\n            : '0 customers'}\n        </p>\n        <nav aria-label=\"Customer pages\" className=\"flex items-center gap-1\">\n          <Button\n            disabled={page === 1}\n            onClick={() => setPage((current) => Math.max(1, current - 1))}\n            size=\"sm\"\n            variant=\"outline\"\n          >\n            Previous\n          </Button>\n          {Array.from({ length: pageCount }, (_, index) => index + 1).map(\n            (number) => (\n              <Button\n                aria-current={page === number ? 'page' : undefined}\n                key={number}\n                onClick={() => setPage(number)}\n                size=\"icon-sm\"\n                variant={page === number ? 'secondary' : 'ghost'}\n              >\n                {number}\n              </Button>\n            )\n          )}\n          <Button\n            disabled={page === pageCount}\n            onClick={() =>\n              setPage((current) => Math.min(pageCount, current + 1))\n            }\n            size=\"sm\"\n            variant=\"outline\"\n          >\n            Next\n          </Button>\n        </nav>\n      </div>\n\n      <Dialog\n        onOpenChange={(open) => {\n          if (!open) {\n            setCustomer(null);\n          }\n        }}\n        open={customer !== null}\n      >\n        <DialogContent className=\"sm:max-w-md\">\n          {customer && (\n            <>\n              <DialogHeader>\n                <DialogTitle>{customer.name}</DialogTitle>\n                <DialogDescription>\n                  Customer details and signup information.\n                </DialogDescription>\n              </DialogHeader>\n              <dl className=\"grid grid-cols-[88px_1fr] gap-x-4 gap-y-3 text-sm\">\n                <dt className=\"text-muted-foreground\">Email</dt>\n                <dd className=\"min-w-0 break-all\">{customer.email}</dd>\n                <dt className=\"text-muted-foreground\">Signed up</dt>\n                <dd>{customer.signedUp}</dd>\n                <dt className=\"text-muted-foreground\">Plan</dt>\n                <dd>{customer.plan}</dd>\n                <dt className=\"text-muted-foreground\">Status</dt>\n                <dd>\n                  <StatusDot status={customer.status} />\n                </dd>\n              </dl>\n              <DialogFooter>\n                <Button\n                  render={\n                    <a\n                      aria-label={`Contact ${customer.name}`}\n                      href={`mailto:${customer.email}`}\n                    >\n                      <MailIcon aria-hidden=\"true\" />\n                      Contact customer\n                    </a>\n                  }\n                />\n              </DialogFooter>\n            </>\n          )}\n        </DialogContent>\n      </Dialog>\n    </section>\n  );\n}\n\nfunction StatusDot({ status }: { readonly status: Status }) {\n  return (\n    <span className=\"inline-flex items-center gap-2 text-sm\">\n      <span\n        aria-hidden=\"true\"\n        className={cn('size-1.5 rounded-full', statusDots[status])}\n      />\n      {status}\n    </span>\n  );\n}\n\ntype Plan = 'Starter' | 'Pro' | 'Team';\ntype Status = 'Active' | 'Pending' | 'Under review' | 'Rejected' | 'Inactive';\n\ntype Customer = {\n  readonly name: string;\n  readonly email: string;\n  readonly signedUp: string;\n  readonly plan: Plan;\n  readonly status: Status;\n};\n\nconst customers: readonly Customer[] = [\n  {\n    name: 'Priya Raman',\n    email: 'priya.raman@lumenlabs.io',\n    signedUp: 'Today, 9:12 AM',\n    plan: 'Pro',\n    status: 'Active',\n  },\n  {\n    name: 'Daniel Okafor',\n    email: 'd.okafor@fastmail.com',\n    signedUp: 'Today, 8:40 AM',\n    plan: 'Starter',\n    status: 'Pending',\n  },\n  {\n    name: 'Mei Lin Chen',\n    email: 'meilin@outlook.com',\n    signedUp: 'Yesterday, 6:05 PM',\n    plan: 'Team',\n    status: 'Active',\n  },\n  {\n    name: 'Tomás Herrera',\n    email: 'tomas.h@gmail.com',\n    signedUp: 'Yesterday, 2:31 PM',\n    plan: 'Pro',\n    status: 'Under review',\n  },\n  {\n    name: 'Aisha Bello',\n    email: 'aisha.bello@proton.me',\n    signedUp: '11 Nov, 4:18 PM',\n    plan: 'Starter',\n    status: 'Rejected',\n  },\n  {\n    name: 'Lucas Moreau',\n    email: 'l.moreau@yahoo.fr',\n    signedUp: '11 Nov, 11:02 AM',\n    plan: 'Team',\n    status: 'Active',\n  },\n  {\n    name: 'Hannah Weiss',\n    email: 'hannah.w@gmail.com',\n    signedUp: '10 Nov, 7:44 PM',\n    plan: 'Pro',\n    status: 'Pending',\n  },\n  {\n    name: 'Kenji Sato',\n    email: 'kenji.sato@icloud.com',\n    signedUp: '10 Nov, 3:15 PM',\n    plan: 'Starter',\n    status: 'Inactive',\n  },\n  {\n    name: 'Olivia Brooks',\n    email: 'olivia@brooks.studio',\n    signedUp: '9 Nov, 10:27 AM',\n    plan: 'Team',\n    status: 'Active',\n  },\n  {\n    name: 'Sofia Alvarez',\n    email: 'sofia@northstar.co',\n    signedUp: '9 Nov, 8:16 AM',\n    plan: 'Pro',\n    status: 'Active',\n  },\n  {\n    name: 'Marcus Johnson',\n    email: 'marcus.j@hey.com',\n    signedUp: '8 Nov, 5:42 PM',\n    plan: 'Team',\n    status: 'Under review',\n  },\n  {\n    name: 'Elena Petrova',\n    email: 'elena@kinetic.design',\n    signedUp: '8 Nov, 1:20 PM',\n    plan: 'Starter',\n    status: 'Pending',\n  },\n  {\n    name: 'Noah Williams',\n    email: 'noah@fieldnotes.app',\n    signedUp: '7 Nov, 4:55 PM',\n    plan: 'Pro',\n    status: 'Active',\n  },\n  {\n    name: 'Fatima Zahra',\n    email: 'fatima.z@proton.me',\n    signedUp: '7 Nov, 11:09 AM',\n    plan: 'Team',\n    status: 'Active',\n  },\n  {\n    name: 'Ethan Park',\n    email: 'ethan.park@icloud.com',\n    signedUp: '6 Nov, 6:33 PM',\n    plan: 'Starter',\n    status: 'Inactive',\n  },\n  {\n    name: 'Amara Nwosu',\n    email: 'amara@woven.studio',\n    signedUp: '6 Nov, 2:08 PM',\n    plan: 'Pro',\n    status: 'Rejected',\n  },\n  {\n    name: 'Gabriel Silva',\n    email: 'gabriel@orbit.dev',\n    signedUp: '5 Nov, 9:47 AM',\n    plan: 'Team',\n    status: 'Active',\n  },\n  {\n    name: 'Leila Haddad',\n    email: 'leila.h@fastmail.com',\n    signedUp: '4 Nov, 3:26 PM',\n    plan: 'Starter',\n    status: 'Pending',\n  },\n];\n\ntype Facet = 'Plan' | 'Status';\nconst facets: readonly Facet[] = ['Plan', 'Status'];\nconst options: Record<Facet, readonly string[]> = {\n  Plan: ['Starter', 'Pro', 'Team'],\n  Status: ['Active', 'Pending', 'Under review', 'Rejected', 'Inactive'],\n};\n\nconst statusDots: Record<Status, string> = {\n  Active: 'bg-green-500',\n  Pending: 'bg-amber-500',\n  'Under review': 'bg-blue-500',\n  Rejected: 'bg-red-500',\n  Inactive: 'bg-muted-foreground/40',\n};\n"
        },
        {
          "path": "content/components/dashboard/dashboard-01/dashboard-charts.tsx",
          "type": "registry:component",
          "target": "components/dashboard-01/dashboard-charts.tsx",
          "content": "'use client';\n\nimport { format, parseISO } from 'date-fns';\nimport {\n  Area,\n  CartesianGrid,\n  ComposedChart,\n  Line,\n  XAxis,\n  YAxis,\n} from 'recharts';\nimport {\n  type ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from '@/components/ui/chart';\nimport { cn } from '@/lib/utils';\n\nconst swap =\n  'ease-[cubic-bezier(0.23,1,0.32,1)] fade-in zoom-in-95 motion-reduce:zoom-in-100 animate-in duration-150';\n\nexport function DashboardCharts({\n  period,\n  from,\n  to,\n}: {\n  period: 'day' | 'week' | 'month' | 'year';\n  from: string;\n  to: string;\n}) {\n  const hourly = from === to;\n  const start = days.findIndex((day) => day.date >= from);\n  const current = days.filter((day) => day.date >= from && day.date <= to);\n  const prior = days.slice(start - current.length, start);\n  const data = hourly\n    ? hours(current[0], prior[0])\n    : current.map((day, index) => ({\n        date: day.date,\n        revenue: day.revenue,\n        priorRevenue: prior[index]?.revenue ?? 0,\n        charges: day.charges,\n        priorCharges: prior[index]?.charges ?? 0,\n      }));\n  const tickStep = Math.max(1, Math.ceil(data.length / 7));\n  const ticks = data\n    .filter((_, index) => index % tickStep === 0)\n    .map((row) => row.date);\n  const tickFormat = hourly ? 'ha' : data.length > 90 ? 'MMM' : 'd MMM';\n  const labelFormat = hourly ? 'EEE, d MMM, ha' : 'EEE, d MMM yyyy';\n\n  return (\n    <div className=\"@container/charts border-b\">\n      <div className=\"grid @4xl/charts:grid-cols-2\">\n        {panels.map((panel) => {\n          const total = data.reduce((sum, row) => sum + row[panel.current], 0);\n          const previous = data.reduce(\n            (sum, row) => sum + row[panel.previous],\n            0\n          );\n          const change = previous ? ((total - previous) / previous) * 100 : 0;\n          const config = {\n            [panel.current]: {\n              label: `This ${period}`,\n              color: 'var(--chart-1)',\n            },\n            [panel.previous]: {\n              label: `Last ${period}`,\n              color: 'var(--chart-2)',\n            },\n          } satisfies ChartConfig;\n\n          return (\n            <section\n              aria-labelledby={`${panel.id}-title`}\n              className=\"@container/panel flex min-h-100 min-w-0 flex-col gap-7 border-t @4xl/charts:border-t-0 @4xl/charts:not-first:border-l p-4 py-7 first:border-t-0 sm:px-7\"\n              id={panel.id}\n              key={panel.id}\n            >\n              <div className=\"relative flex flex-col gap-2\">\n                <h2\n                  className=\"text-[0.9375rem] text-muted-foreground\"\n                  id={`${panel.id}-title`}\n                >\n                  {panel.title}\n                </h2>\n                <div className=\"flex flex-wrap items-baseline gap-2.5\">\n                  <p className=\"font-semibold text-[1.75rem] tabular-nums tracking-tight\">\n                    {panel.currency ? '$' : ''}\n                    {total.toLocaleString('en-US')}\n                  </p>\n                  <p\n                    className={cn(\n                      'font-medium text-[0.8125rem] tabular-nums',\n                      change < 0\n                        ? 'text-red-600 dark:text-red-400'\n                        : 'text-green-600 dark:text-green-400'\n                    )}\n                  >\n                    {change > 0 ? '+' : ''}\n                    {change.toFixed(1)}%\n                  </p>\n                  <p className=\"text-[0.8125rem] text-muted-foreground\">\n                    vs. last {period}\n                  </p>\n                </div>\n                <div className=\"@lg/panel:absolute @lg/panel:top-1 @lg/panel:right-0 flex items-center gap-4 text-[0.8125rem]\">\n                  <div className=\"flex items-center gap-2\">\n                    <span\n                      aria-hidden=\"true\"\n                      className=\"h-0.75 w-2.5 rounded-full bg-chart-1\"\n                    />\n                    This {period}\n                  </div>\n                  <div className=\"flex items-center gap-2\">\n                    <span\n                      aria-hidden=\"true\"\n                      className=\"h-0.75 w-2.5 rounded-full bg-chart-2\"\n                    />\n                    Last {period}\n                  </div>\n                </div>\n              </div>\n              {data.length ? (\n                <ChartContainer\n                  className={cn(swap, 'aspect-auto h-63 w-full')}\n                  config={config}\n                  key={`${from}-${to}`}\n                >\n                  <ComposedChart\n                    accessibilityLayer\n                    data={data}\n                    margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n                  >\n                    <CartesianGrid\n                      stroke=\"var(--border)\"\n                      strokeDasharray=\"3 4\"\n                      vertical={false}\n                    />\n                    <XAxis\n                      axisLine={false}\n                      dataKey=\"date\"\n                      height={40}\n                      minTickGap={24}\n                      tickFormatter={(date: string) =>\n                        format(parseISO(date), tickFormat)\n                      }\n                      tickLine={false}\n                      tickMargin={16}\n                      ticks={ticks}\n                    />\n                    <YAxis\n                      axisLine={false}\n                      domain={[0, 'auto']}\n                      tickCount={4}\n                      tickFormatter={(value: number) =>\n                        `${panel.currency ? '$' : ''}${value >= 1000 ? `${value / 1000}k` : value}`\n                      }\n                      tickLine={false}\n                      tickMargin={8}\n                      width={48}\n                    />\n                    <ChartTooltip\n                      content={\n                        <ChartTooltipContent\n                          className=\"w-47.5 rounded-[10px] bg-popover px-3 py-2.5\"\n                          formatter={(value, name) => (\n                            <div className=\"flex w-full items-center gap-2\">\n                              <span\n                                aria-hidden=\"true\"\n                                className={cn(\n                                  'size-2 shrink-0 rounded-xs',\n                                  name === panel.current\n                                    ? 'bg-chart-1'\n                                    : 'bg-chart-2'\n                                )}\n                              />\n                              <span className=\"flex-1 text-muted-foreground\">\n                                {name === panel.current\n                                  ? `This ${period}`\n                                  : `Last ${period}`}\n                              </span>\n                              <span className=\"font-medium text-foreground tabular-nums\">\n                                {panel.currency ? '$' : ''}\n                                {Number(value).toLocaleString('en-US')}\n                              </span>\n                            </div>\n                          )}\n                          labelClassName=\"font-normal text-muted-foreground\"\n                          labelFormatter={(label) =>\n                            typeof label === 'string'\n                              ? format(parseISO(label), labelFormat)\n                              : label\n                          }\n                        />\n                      }\n                      cursor={{\n                        stroke: 'var(--chart-1)',\n                        strokeDasharray: '3 3',\n                      }}\n                    />\n                    <Area\n                      activeDot={false}\n                      dataKey={panel.current}\n                      fill=\"var(--chart-1)\"\n                      fillOpacity={0.1}\n                      isAnimationActive={false}\n                      stroke=\"none\"\n                      tooltipType=\"none\"\n                      type=\"linear\"\n                    />\n                    <Line\n                      activeDot={{\n                        r: 5,\n                        fill: 'var(--chart-1)',\n                        stroke: 'var(--background)',\n                        strokeWidth: 2,\n                      }}\n                      dataKey={panel.current}\n                      dot={data.length === 1}\n                      isAnimationActive={false}\n                      stroke=\"var(--chart-1)\"\n                      strokeWidth={2.5}\n                      type=\"linear\"\n                    />\n                    <Line\n                      activeDot={{\n                        r: 4,\n                        fill: 'var(--chart-2)',\n                        stroke: 'var(--background)',\n                        strokeWidth: 2,\n                      }}\n                      dataKey={panel.previous}\n                      dot={data.length === 1}\n                      isAnimationActive={false}\n                      stroke=\"var(--chart-2)\"\n                      strokeWidth={2}\n                      type=\"linear\"\n                    />\n                  </ComposedChart>\n                </ChartContainer>\n              ) : (\n                <p className=\"flex flex-1 items-center justify-center text-muted-foreground text-sm\">\n                  No data for this date range.\n                </p>\n              )}\n            </section>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n\nconst panels = [\n  {\n    id: 'revenue',\n    title: 'Revenue',\n    current: 'revenue',\n    previous: 'priorRevenue',\n    currency: true,\n  },\n  {\n    id: 'charges',\n    title: 'Charges',\n    current: 'charges',\n    previous: 'priorCharges',\n    currency: false,\n  },\n] as const;\n\ntype Day = { date: string; revenue: number; charges: number };\n\n// Seeded LCG so every render draws the same two years of demo data.\nlet seed = 20_251_116;\nconst random = () => {\n  seed = (Math.imul(seed, 1_664_525) + 1_013_904_223) >>> 0;\n  return seed / 2 ** 32;\n};\n\nconst days: Day[] = Array.from({ length: 731 }, (_, index) => {\n  const date = new Date(Date.UTC(2023, 10, 17 + index));\n  const growth = 1 + index / 731;\n  const weekend = date.getUTCDay() % 6 === 0 ? 0.62 : 1;\n  const season =\n    1 + 0.12 * Math.sin(((date.getUTCMonth() - 3) / 12) * Math.PI * 2);\n  const spike = random() < 0.04 ? 1.6 : 1;\n  const revenue = Math.round(\n    3400 * growth * weekend * season * spike * (0.82 + random() * 0.36)\n  );\n  return {\n    date: date.toISOString().slice(0, 10),\n    revenue,\n    charges: Math.round((revenue / 13.5) * (0.9 + random() * 0.2)),\n  };\n});\n\nconst activity = [\n  0.54, 0.46, 0.38, 0.35, 0.41, 0.58, 0.74, 0.97, 1.12, 1.38, 1.21, 1.53, 1.29,\n  1.41, 1.67, 1.48, 1.31, 1.58, 1.36, 1.19, 1.04, 0.86, 0.72, 0.63,\n];\n\n// Split a day's totals across 24 hours. Noise is keyed on the date so the\n// same day always draws the same curve, and the hourly shares sum to the day.\nfunction hours(day: Day | undefined, previous: Day | undefined) {\n  if (!(day && previous)) {\n    return [];\n  }\n  const weights = [day, previous].map((row) => {\n    const key = Number(row.date.replaceAll('-', ''));\n    const noisy = activity.map(\n      (weight, hour) =>\n        weight * (0.8 + 0.4 * ((Math.sin(key + hour * 7.31) + 1) / 2))\n    );\n    const total = noisy.reduce((sum, weight) => sum + weight, 0);\n    return noisy.map((weight) => weight / total);\n  });\n  let share = 0;\n  let priorShare = 0;\n  return activity.map((_, hour) => {\n    const from = share;\n    const priorFrom = priorShare;\n    share += weights[0][hour];\n    priorShare += weights[1][hour];\n    return {\n      date: `${day.date}T${String(hour).padStart(2, '0')}:00:00`,\n      revenue: Math.round(day.revenue * share) - Math.round(day.revenue * from),\n      priorRevenue:\n        Math.round(previous.revenue * priorShare) -\n        Math.round(previous.revenue * priorFrom),\n      charges: Math.round(day.charges * share) - Math.round(day.charges * from),\n      priorCharges:\n        Math.round(previous.charges * priorShare) -\n        Math.round(previous.charges * priorFrom),\n    };\n  });\n}\n"
        },
        {
          "path": "content/components/dashboard/dashboard-01/app/page.tsx",
          "type": "registry:page",
          "target": "app/page.tsx",
          "content": "'use client';\n\nimport { useState } from 'react';\nimport { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport { CustomersTable } from '@/components/dashboard-01/customers-table';\nimport { DashboardCharts } from '@/components/dashboard-01/dashboard-charts';\nimport { DashboardHeader } from '@/components/dashboard-01/dashboard-header';\nimport { DashboardSidebar } from '@/components/dashboard-01/dashboard-sidebar';\n\nexport default function Page() {\n  const [period, setPeriod] = useState<'day' | 'week' | 'month' | 'year'>(\n    'week'\n  );\n  const [range, setRange] = useState({ from: '2025-11-10', to: '2025-11-16' });\n\n  const bindTheme = (node: HTMLElement | null) => {\n    if (!node) {\n      return;\n    }\n    const onKey = (event: KeyboardEvent) => {\n      const target = event.target as HTMLElement;\n      if (\n        event.key === 'D' &&\n        event.shiftKey &&\n        !target.isContentEditable &&\n        !['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)\n      ) {\n        document.documentElement.classList.toggle('dark');\n      }\n    };\n    window.addEventListener('keydown', onKey);\n    return () => window.removeEventListener('keydown', onKey);\n  };\n\n  return (\n    <SidebarProvider\n      className=\"isolate min-h-dvh bg-background font-sans text-foreground antialiased [--chart-1:#3b82f6] [--chart-2:#d4d4d8] [--font-sans:system-ui,sans-serif] [--sidebar-width-icon:3.5rem]! [--sidebar-width:15.5rem]! **:data-[slot=sidebar-group-label]:transition-none **:data-[slot=sidebar-menu-button]:transition-none **:data-[slot=sidebar-container]:ease-[cubic-bezier(0.23,1,0.32,1)] **:data-[slot=sidebar-gap]:ease-[cubic-bezier(0.23,1,0.32,1)] dark:[--chart-2:#3f3f46] dark:[--sidebar-accent:#1a1a1a] dark:[--sidebar:#0d0d0d]\"\n      defaultOpen={false}\n    >\n      <a\n        className=\"sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-50 focus:rounded-lg focus:bg-background focus:p-3\"\n        href=\"#overview\"\n      >\n        Skip to dashboard\n      </a>\n      <DashboardSidebar />\n      <SidebarInset className=\"min-w-0\" id=\"overview\" ref={bindTheme}>\n        <DashboardHeader\n          onPeriodChange={setPeriod}\n          onRangeChange={setRange}\n          period={period}\n          range={range}\n        />\n        <div className=\"@container/metrics border-b\">\n          <dl className=\"grid @3xl/metrics:grid-cols-4 grid-cols-2\">\n            {metrics.map((metric) => (\n              <div\n                className=\"flex min-w-0 flex-col justify-center gap-2 border-b @3xl/metrics:border-b-0 @3xl/metrics:not-first:border-l @3xl/metrics:px-7 px-4 py-6 even:border-l [&:nth-last-child(-n+2)]:border-b-0\"\n                key={metric.label}\n              >\n                <dt className=\"truncate text-muted-foreground text-sm sm:text-[0.9375rem]\">\n                  {metric.label}\n                </dt>\n                <dd className=\"flex flex-wrap items-baseline gap-2.5\">\n                  <span className=\"font-semibold text-[1.75rem] tabular-nums tracking-tight\">\n                    {metric.value}\n                  </span>\n                  <span\n                    className={cn(\n                      'font-medium text-[0.8125rem] tabular-nums',\n                      metric.changeClass\n                    )}\n                  >\n                    {metric.change}\n                  </span>\n                </dd>\n              </div>\n            ))}\n          </dl>\n        </div>\n        <DashboardCharts from={range.from} period={period} to={range.to} />\n        <CustomersTable />\n      </SidebarInset>\n    </SidebarProvider>\n  );\n}\n\nconst metrics = [\n  {\n    label: 'New customers',\n    value: '1,284',\n    change: '+8.2%',\n    changeClass: 'text-green-600 dark:text-green-400',\n  },\n  {\n    label: 'Success rate',\n    value: '97.4%',\n    change: '+0.6%',\n    changeClass: 'text-green-600 dark:text-green-400',\n  },\n  {\n    label: 'Failure rate',\n    value: '2.6%',\n    change: '-0.6%',\n    changeClass: 'text-red-600 dark:text-red-400',\n  },\n  {\n    label: 'Refunds',\n    value: '38',\n    change: '0.0%',\n    changeClass: 'text-muted-foreground',\n  },\n];\n"
        }
      ],
      "categories": [
        "dashboard"
      ]
    },
    {
      "name": "dialog-01",
      "type": "registry:block",
      "title": "Dialog Confirmation",
      "description": "A dialog confirmation block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dialog"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-01.tsx",
          "type": "registry:component",
          "target": "components/dialog-01.tsx",
          "content": "'use client';\n\nimport { Check } from 'lucide-react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\n\nexport default function Dialog01() {\n  return (\n    <Dialog defaultOpen>\n      <DialogTrigger render={<Button variant=\"outline\" />}>\n        Show Dialog\n      </DialogTrigger>\n\n      <DialogContent className=\"flex flex-col items-center sm:max-w-sm\">\n        <div className=\"flex justify-center\">\n          <div className=\"mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100\">\n            <Check className=\"h-6 w-6 text-green-600\" />\n          </div>\n        </div>\n\n        <DialogHeader className=\"gap-0 text-center\">\n          <DialogTitle className=\"text-balance text-center\">\n            Payment successful\n          </DialogTitle>\n          <DialogDescription className=\"mx-auto mt-2 text-pretty text-center sm:max-w-[90%]\">\n            Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur\n            amet labore.\n          </DialogDescription>\n        </DialogHeader>\n        <DialogFooter className=\"w-full sm:justify-center\">\n          <DialogClose render={<Button className=\"w-full\" />}>\n            Go back to dashboard\n          </DialogClose>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-02",
      "type": "registry:block",
      "title": "Dialog with Warning Icon",
      "description": "A dialog with warning icon block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dialog"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-02.tsx",
          "type": "registry:component",
          "target": "components/dialog-02.tsx",
          "content": "'use client';\n\nimport { Check } from 'lucide-react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\n\nexport default function Dialog04() {\n  return (\n    <Dialog defaultOpen>\n      <DialogTrigger render={<Button variant=\"outline\" />}>\n        Show Dialog\n      </DialogTrigger>\n\n      <DialogContent className=\"flex flex-col items-center sm:max-w-sm\">\n        <div className=\"flex justify-center\">\n          <div className=\"mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100\">\n            <Check className=\"h-6 w-6 text-green-600\" />\n          </div>\n        </div>\n\n        <DialogHeader className=\"gap-0 text-center\">\n          <DialogTitle className=\"text-balance text-center\">\n            Payment successful\n          </DialogTitle>\n          <DialogDescription className=\"mx-auto mt-2 text-pretty text-center sm:max-w-[90%]\">\n            Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur\n            amet labore.\n          </DialogDescription>\n        </DialogHeader>\n        <DialogFooter className=\"flex w-full flex-col gap-2 sm:flex-row sm:gap-3\">\n          <DialogClose\n            render={<Button className=\"w-full sm:w-1/2\" variant=\"default\" />}\n          >\n            Deactivate\n          </DialogClose>\n          <DialogClose\n            render={<Button className=\"w-full sm:w-1/2\" variant=\"outline\" />}\n          >\n            Cancel\n          </DialogClose>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-03",
      "type": "registry:block",
      "title": "Dialog with Input Field",
      "description": "A dialog with input field block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dialog"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-03.tsx",
          "type": "registry:component",
          "target": "components/dialog-03.tsx",
          "content": "'use client';\nimport { AlertTriangleIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\n\nexport default function Dialog02() {\n  const [open, setOpen] = useState(true);\n\n  return (\n    <Dialog onOpenChange={setOpen} open={open}>\n      <DialogTrigger render={<Button variant=\"destructive\" />}>\n        Deactivate\n      </DialogTrigger>\n      <DialogContent className=\"sm:max-w-lg\">\n        <div className=\"flex items-start space-x-4\">\n          <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-red-100\">\n            <AlertTriangleIcon className=\"h-6 w-6 text-red-600\" />\n          </div>\n          <DialogHeader>\n            <DialogTitle>Deactivate account</DialogTitle>\n            <DialogDescription>\n              Are you sure you want to deactivate your account? All of your data\n              will be permanently removed. This action cannot be undone.\n            </DialogDescription>\n          </DialogHeader>\n        </div>\n        <DialogFooter>\n          <DialogClose render={<Button variant=\"outline\" />}>\n            Cancel\n          </DialogClose>\n          <Button onClick={() => setOpen(false)} variant=\"destructive\">\n            Deactivate\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-04",
      "type": "registry:block",
      "title": "Dialog with Two Buttons",
      "description": "A dialog with two buttons block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dialog",
        "input",
        "label"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/dialogs/dialog-04.tsx",
          "type": "registry:component",
          "target": "components/dialog-04.tsx",
          "content": "'use client';\n\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nexport default function Dialog03() {\n  return (\n    <Dialog defaultOpen>\n      <div className=\"flex items-center justify-center py-36\">\n        <DialogTrigger render={<Button />}>Edit Profile</DialogTrigger>\n      </div>\n\n      <DialogContent className=\"sm:max-w-md\">\n        <DialogHeader>\n          <DialogTitle>Edit Profile</DialogTitle>\n          <DialogDescription>\n            Make changes to your profile here. Click save when you&apos;re done.\n          </DialogDescription>\n        </DialogHeader>\n        <div className=\"space-y-2\">\n          <Label className=\"font-medium text-sm\" htmlFor=\"username\">\n            Username\n          </Label>\n          <Input\n            id=\"username\"\n            name=\"username\"\n            placeholder=\"Your username\"\n            type=\"text\"\n          />\n        </div>\n        <DialogFooter>\n          <Button className=\"w-full\" type=\"submit\">\n            Save Changes\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-05",
      "type": "registry:block",
      "title": "Dialog with Password Confirmation",
      "description": "A dialog with password confirmation block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "alert-dialog",
        "button",
        "input",
        "label"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-05.tsx",
          "type": "registry:component",
          "target": "components/dialog-05.tsx",
          "content": "'use client';\n\nimport { EyeIcon, EyeOffIcon } from 'lucide-react';\nimport { type FormEvent, useState } from 'react';\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n} from '@/components/ui/alert-dialog';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nexport default function Dialog05() {\n  const [isOpen, setIsOpen] = useState(true);\n  const [isVisible, setIsVisible] = useState<boolean>(false);\n\n  const toggleVisibility = () => setIsVisible((prevState) => !prevState);\n  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    setIsOpen(false);\n  };\n\n  return (\n    <AlertDialog onOpenChange={setIsOpen} open={isOpen}>\n      <AlertDialogTrigger render={<Button variant=\"destructive\" />}>\n        Delete Workspace\n      </AlertDialogTrigger>\n      <AlertDialogContent className=\"sm:max-w-md\">\n        <AlertDialogHeader>\n          <AlertDialogTitle className=\"text-balance\">\n            Delete workspace\n          </AlertDialogTitle>\n          <AlertDialogDescription className=\"text-pretty\">\n            All workspace data will be permanently deleted. There is no coming\n            back after you press delete.\n          </AlertDialogDescription>\n        </AlertDialogHeader>\n        <form onSubmit={handleSubmit}>\n          <div>\n            <Label className=\"font-medium text-sm\" htmlFor=\"delete-workspace\">\n              Confirm password\n            </Label>\n            <div className=\"relative mt-2\">\n              <Input\n                className=\"pe-9\"\n                id=\"delete-workspace\"\n                name=\"delete-workspace\"\n                placeholder=\"Password\"\n                required\n                type={isVisible ? 'text' : 'password'}\n              />\n              <button\n                aria-controls=\"delete-workspace\"\n                aria-label={isVisible ? 'Hide password' : 'Show password'}\n                aria-pressed={isVisible}\n                className=\"absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-md\"\n                onClick={toggleVisibility}\n                type=\"button\"\n              >\n                {isVisible ? (\n                  <EyeOffIcon aria-hidden=\"true\" size={16} />\n                ) : (\n                  <EyeIcon aria-hidden=\"true\" size={16} />\n                )}\n              </button>\n            </div>\n          </div>\n          <AlertDialogFooter className=\"mt-4\">\n            <AlertDialogCancel>Cancel</AlertDialogCancel>\n            <Button type=\"submit\" variant=\"destructive\">\n              Delete workspace permanently\n            </Button>\n          </AlertDialogFooter>\n        </form>\n      </AlertDialogContent>\n    </AlertDialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-06",
      "type": "registry:block",
      "title": "Dialog with Privacy Toggle",
      "description": "A dialog with privacy toggle block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dialog",
        "input",
        "label",
        "switch"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/dialogs/dialog-06.tsx",
          "type": "registry:component",
          "target": "components/dialog-06.tsx",
          "content": "'use client';\n\nimport { useState } from 'react';\n\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Switch } from '@/components/ui/switch';\n\nexport default function Dialog06() {\n  const [open, setOpen] = useState(true);\n\n  return (\n    <Dialog onOpenChange={setOpen} open={open}>\n      <DialogTrigger render={<Button onClick={() => setOpen(true)} />}>\n        Create Workspace\n      </DialogTrigger>\n      <DialogContent className=\"p-0 sm:max-w-lg\">\n        <DialogHeader className=\"px-6 pt-4\">\n          <DialogTitle className=\"text-balance font-semibold text-foreground text-lg\">\n            Create workspace\n          </DialogTitle>\n          <DialogDescription className=\"mt-2 text-pretty text-muted-foreground text-sm leading-6\">\n            Workspaces are shared environments where teams can connect to data\n            sources, run queries and create reports.\n          </DialogDescription>\n        </DialogHeader>\n\n        <form action=\"#\" method=\"POST\">\n          <div className=\"px-6 pb-4\">\n            <Label className=\"font-medium text-sm\" htmlFor=\"workspace-name\">\n              Workspace Name<span className=\"text-destructive\">*</span>\n            </Label>\n            <Input\n              className=\"mt-2\"\n              id=\"workspace-name\"\n              name=\"workspace-name\"\n              placeholder=\"My workspace\"\n              required\n              type=\"text\"\n            />\n            <Button className=\"mt-4 w-full\" type=\"submit\">\n              Create workspace\n            </Button>\n          </div>\n          <div className=\"rounded-b-md border-t bg-muted px-6 py-4\">\n            <div className=\"flex items-start space-x-3\">\n              <div className=\"mt-1 pt-0.5\">\n                <Switch\n                  id=\"enable-private-workspace\"\n                  name=\"enable-private-workspace\"\n                />\n              </div>\n              <div>\n                <Label\n                  className=\"font-medium text-sm\"\n                  htmlFor=\"enable-private-workspace\"\n                >\n                  Set workspace to private\n                </Label>\n                <p className=\"text-pretty text-muted-foreground text-sm\">\n                  Only those invited can access or view\n                </p>\n              </div>\n            </div>\n          </div>\n        </form>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-07",
      "type": "registry:block",
      "title": "Dialog with Member List",
      "description": "A dialog with member list block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "alert-dialog",
        "button",
        "input",
        "label"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-07.tsx",
          "type": "registry:component",
          "target": "components/dialog-07.tsx",
          "content": "'use client';\n\nimport { EyeIcon, EyeOffIcon } from 'lucide-react';\nimport { type FormEvent, useState } from 'react';\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n} from '@/components/ui/alert-dialog';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nexport default function Dialog08() {\n  const [open, setOpen] = useState(true);\n  const [isVisible, setIsVisible] = useState<boolean>(false);\n\n  const toggleVisibility = () => setIsVisible((prevState) => !prevState);\n  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    setOpen(false);\n  };\n\n  return (\n    <AlertDialog onOpenChange={setOpen} open={open}>\n      <AlertDialogTrigger render={<Button variant=\"destructive\" />}>\n        Deactivate 2FA\n      </AlertDialogTrigger>\n      <AlertDialogContent className=\"sm:max-w-lg\">\n        <AlertDialogHeader>\n          <AlertDialogTitle className=\"text-balance\">\n            Deactivate two-step authentication\n          </AlertDialogTitle>\n          <AlertDialogDescription className=\"text-pretty\">\n            Enter your password to deactivate the two-step authentication login.\n            Make sure to have your smartphone ready.\n          </AlertDialogDescription>\n        </AlertDialogHeader>\n        <form className=\"space-y-4\" onSubmit={handleSubmit}>\n          <div>\n            <Label className=\"font-medium text-sm\" htmlFor=\"email\">\n              Email\n            </Label>\n            <Input\n              className=\"mt-2\"\n              disabled\n              id=\"email\"\n              name=\"email\"\n              placeholder=\"ephraim@blocks.so\"\n              type=\"email\"\n            />\n          </div>\n          <div>\n            <Label className=\"font-medium text-sm\" htmlFor=\"password\">\n              Confirm password\n            </Label>\n            <div className=\"relative mt-2\">\n              <Input\n                className=\"pe-9\"\n                id=\"password\"\n                name=\"password\"\n                placeholder=\"Password\"\n                required\n                type={isVisible ? 'text' : 'password'}\n              />\n              <button\n                aria-controls=\"password\"\n                aria-label={isVisible ? 'Hide password' : 'Show password'}\n                aria-pressed={isVisible}\n                className=\"absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-md\"\n                onClick={toggleVisibility}\n                type=\"button\"\n              >\n                {isVisible ? (\n                  <EyeOffIcon aria-hidden=\"true\" size={16} />\n                ) : (\n                  <EyeIcon aria-hidden=\"true\" size={16} />\n                )}\n              </button>\n            </div>\n          </div>\n\n          <AlertDialogFooter>\n            <AlertDialogCancel>Cancel</AlertDialogCancel>\n            <Button type=\"submit\" variant=\"destructive\">\n              Deactivate\n            </Button>\n          </AlertDialogFooter>\n        </form>\n      </AlertDialogContent>\n    </AlertDialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-08",
      "type": "registry:block",
      "title": "Dialog with Email and Password Fields",
      "description": "A dialog with email and password fields block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "badge",
        "button",
        "dialog",
        "input"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-08.tsx",
          "type": "registry:component",
          "target": "components/dialog-08.tsx",
          "content": "'use client';\n\nimport { UserPlus } from 'lucide-react';\nimport { useState } from 'react';\n\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\n\nconst members = [\n  {\n    name: 'Ephraim Duncan',\n    email: 'ephraim@documenso.com',\n    avatarUrl: 'https://blocks.so/avatar-01.png',\n    initials: 'ED',\n    status: 'member',\n  },\n  {\n    name: 'Lucas Smith',\n    email: 'lucas@documenso.com',\n    avatarUrl: 'https://blocks.so/avatar-03.png',\n    initials: 'LS',\n    status: 'member',\n  },\n  {\n    name: 'Timur Ercan',\n    email: 'timur@documenso.com',\n    avatarUrl: 'https://blocks.so/avatar-02.jpg',\n    initials: 'TE',\n    status: 'member',\n  },\n  {\n    name: 'Catalin Pit',\n    email: 'catalin@documenso.com',\n    avatarUrl: 'https://blocks.so/avatar-04.jpg',\n    initials: 'CP',\n    status: 'member',\n  },\n];\n\nexport default function Dialog07() {\n  const [open, setOpen] = useState(true);\n\n  return (\n    <Dialog onOpenChange={setOpen} open={open}>\n      <DialogTrigger render={<Button onClick={() => setOpen(true)} />}>\n        Invite members\n      </DialogTrigger>\n      <DialogContent className=\"sm:max-w-2xl\">\n        <DialogHeader>\n          <DialogTitle className=\"text-balance font-semibold text-foreground\">\n            Invite members\n          </DialogTitle>\n          <DialogDescription className=\"text-pretty text-muted-foreground text-sm leading-6\">\n            Add new team members to your workspace. Please consider your\n            organization&apos;s policies when adding external people.\n          </DialogDescription>\n        </DialogHeader>\n        <form>\n          <div className=\"flex w-full items-center space-x-2\">\n            <div className=\"relative flex-1\">\n              <UserPlus className=\"-translate-y-1/2 absolute top-1/2 left-3 h-4 w-4 text-muted-foreground\" />\n              <Input\n                className=\"h-10 pl-9\"\n                id=\"inviteEmail\"\n                placeholder=\"Add email...\"\n                type=\"email\"\n              />\n            </div>\n            <Button className=\"h-10\" type=\"submit\">\n              Invite\n            </Button>\n          </div>\n        </form>\n        <h4 className=\"mt-4 text-balance font-medium text-foreground text-sm\">\n          People with existing access\n        </h4>\n        <ul className=\"divide-y\">\n          {members.map((member) => (\n            <li\n              className=\"flex items-center justify-between py-2.5\"\n              key={member.name}\n            >\n              <div className=\"flex items-center space-x-3\">\n                <Avatar className=\"h-9 w-9\">\n                  <AvatarImage alt={member.name} src={member.avatarUrl} />\n                  <AvatarFallback>{member.initials}</AvatarFallback>\n                </Avatar>\n                <span className=\"font-medium text-foreground\">\n                  {member.name}\n                </span>\n              </div>\n              <Badge\n                className=\"bg-background font-medium text-xs\"\n                variant=\"outline\"\n              >\n                {member.status}\n              </Badge>\n            </li>\n          ))}\n        </ul>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-09",
      "type": "registry:block",
      "title": "Dialog with Link Share and Toggles",
      "description": "A dialog with link share and toggles block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dialog",
        "input",
        "label",
        "switch",
        "tooltip"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-09.tsx",
          "type": "registry:component",
          "target": "components/dialog-09.tsx",
          "content": "'use client';\n\nimport { CheckIcon, CopyIcon, ExternalLink, Link, Share2 } from 'lucide-react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Switch } from '@/components/ui/switch';\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from '@/components/ui/tooltip';\nimport { cn } from '@/lib/utils';\n\nexport default function Dialog09() {\n  const [copied, setCopied] = useState<boolean>(false);\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  const handleCopy = () => {\n    if (inputRef.current) {\n      navigator.clipboard.writeText(inputRef.current.value);\n      setCopied(true);\n      setTimeout(() => setCopied(false), 1500);\n    }\n  };\n\n  return (\n    <Dialog defaultOpen>\n      <DialogTrigger render={<Button />}>\n        <Share2 className=\"mr-2 h-4 w-4\" />\n        Share\n      </DialogTrigger>\n      <DialogContent className=\"sm:max-w-md\">\n        <DialogHeader>\n          <DialogTitle>Share & Collaborate</DialogTitle>\n          <p className=\"text-pretty text-muted-foreground text-sm\">\n            Share this project with your team to collaborate on it.\n          </p>\n        </DialogHeader>\n        <div className=\"space-y-4 py-4\">\n          <div className=\"flex items-center justify-between space-x-2\">\n            <div className=\"flex items-center space-x-2\">\n              <span>Enable comments and suggestions</span>\n            </div>\n            <Switch id=\"comments\" />\n          </div>\n\n          <div className=\"space-y-1.5\">\n            <Label className=\"sr-only\" htmlFor=\"share-link\">\n              Share Link\n            </Label>\n            <div className=\"relative\">\n              <Input\n                className=\"pe-9\"\n                id=\"share-link\"\n                readOnly\n                ref={inputRef}\n                value=\"https://writer.so/app/projects/123?share=true\"\n              />\n              <TooltipProvider delay={0}>\n                <Tooltip>\n                  <TooltipTrigger\n                    render={\n                      <button\n                        aria-label={copied ? 'Copied' : 'Copy to clipboard'}\n                        className=\"absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-md text-muted-foreground/80 outline-none transition-[color,box-shadow] hover:text-foreground focus:z-10 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed\"\n                        disabled={copied}\n                        onClick={handleCopy}\n                      />\n                    }\n                  >\n                    <div\n                      className={cn(\n                        'transition-[transform,opacity] duration-200 ease-out',\n                        copied ? 'scale-100 opacity-100' : 'scale-0 opacity-0'\n                      )}\n                    >\n                      <CheckIcon\n                        aria-hidden=\"true\"\n                        className=\"text-primary\"\n                        size={16}\n                      />\n                    </div>\n                    <div\n                      className={cn(\n                        'absolute transition-[transform,opacity] duration-200 ease-out',\n                        copied ? 'scale-0 opacity-0' : 'scale-100 opacity-100'\n                      )}\n                    >\n                      <CopyIcon aria-hidden=\"true\" size={16} />\n                    </div>\n                  </TooltipTrigger>\n                  <TooltipContent className=\"px-2 py-1 text-xs\">\n                    {copied ? 'Copied!' : 'Copy to clipboard'}\n                  </TooltipContent>\n                </Tooltip>\n              </TooltipProvider>\n            </div>\n          </div>\n\n          <div className=\"flex space-x-2\">\n            <Button className=\"flex-1 gap-2\" onClick={handleCopy}>\n              <Link className=\"h-4 w-4\" />\n              Copy Link\n            </Button>\n            <Button className=\"flex-1 gap-2\" variant=\"outline\">\n              <ExternalLink className=\"h-4 w-4\" />\n              Preview\n            </Button>\n          </div>\n        </div>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-10",
      "type": "registry:block",
      "title": "Dialog with Calendar and Time Picker",
      "description": "A dialog with calendar and time picker block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "calendar",
        "dialog",
        "input",
        "label",
        "popover",
        "select",
        "textarea"
      ],
      "dependencies": [
        "date-fns",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-10.tsx",
          "type": "registry:component",
          "target": "components/dialog-10.tsx",
          "content": "'use client';\n\nimport { format } from 'date-fns';\nimport { CalendarIcon } from 'lucide-react';\nimport { useMemo, useState } from 'react';\n\nimport { Button } from '@/components/ui/button';\nimport { Calendar } from '@/components/ui/calendar';\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from '@/components/ui/popover';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Textarea } from '@/components/ui/textarea';\nimport { cn } from '@/lib/utils';\n\nexport default function Dialog10() {\n  const [date, setDate] = useState<Date | undefined>(new Date());\n  const [startTime, setStartTime] = useState('09:00');\n\n  const timeOptions = useMemo(() => {\n    const options = [];\n    for (let hour = 0; hour <= 23; hour++) {\n      for (let minute = 0; minute < 60; minute += 30) {\n        const formattedHour = hour.toString().padStart(2, '0');\n        const formattedMinute = minute.toString().padStart(2, '0');\n        const value = `${formattedHour}:${formattedMinute}`;\n        const tempDate = new Date(2000, 0, 1, hour, minute);\n        const label = format(tempDate, 'h:mm a');\n        options.push({ value, label });\n      }\n    }\n\n    if (!options.find((opt) => opt.value === '23:59')) {\n      const endOfDay = new Date(2000, 0, 1, 23, 59);\n      options.push({ value: '23:59', label: format(endOfDay, 'h:mm a') });\n    }\n\n    return options;\n  }, []);\n  return (\n    <Dialog defaultOpen>\n      <DialogTrigger render={<Button />}>Show dialog</DialogTrigger>\n      <DialogContent className=\"gap-0 p-0 sm:max-w-lg\">\n        <DialogHeader className=\"border-b px-6 py-4 pt-5\">\n          <DialogTitle>Schedule a Meeting</DialogTitle>\n        </DialogHeader>\n\n        <form action=\"#\" method=\"POST\">\n          <div className=\"space-y-6 p-6\">\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"title\">Meeting Title</Label>\n              <Input\n                id=\"title\"\n                name=\"title\"\n                placeholder=\"e.g., Project Kickoff\"\n              />\n            </div>\n\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"attendees\">Attendees</Label>\n              <Input\n                id=\"attendees\"\n                name=\"attendees\"\n                placeholder=\"user1@example.com, user2@example.com\"\n              />\n              <p className=\"text-pretty text-muted-foreground text-xs\">\n                Enter email addresses separated by commas.\n              </p>\n            </div>\n\n            <div className=\"grid grid-cols-3 gap-4\">\n              <div className=\"col-span-2 space-y-2\">\n                <Label htmlFor=\"date\">Date</Label>\n                <Popover>\n                  <PopoverTrigger\n                    render={\n                      <Button\n                        className={cn(\n                          'w-full justify-start text-left font-normal',\n                          !date && 'text-muted-foreground'\n                        )}\n                        id=\"date\"\n                        variant={'outline'}\n                      />\n                    }\n                  >\n                    <CalendarIcon className=\"mr-1 h-4 w-4 shrink-0\" />{' '}\n                    {date ? format(date, 'PPP') : <span>Pick a date</span>}\n                  </PopoverTrigger>\n                  <PopoverContent align=\"start\" className=\"w-auto p-0\">\n                    <Calendar\n                      autoFocus\n                      mode=\"single\"\n                      onSelect={setDate}\n                      selected={date}\n                    />\n                  </PopoverContent>\n                </Popover>\n              </div>\n\n              <div className=\"space-y-2\">\n                <Label htmlFor=\"time\">Time</Label>\n                <Select\n                  items={timeOptions}\n                  onValueChange={(value) => {\n                    if (value) {\n                      setStartTime(value);\n                    }\n                  }}\n                  value={startTime}\n                >\n                  <SelectTrigger className=\"w-full\" id=\"time\">\n                    <SelectValue placeholder=\"Select time\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    {timeOptions.map((option) => (\n                      <SelectItem key={option.value} value={option.value}>\n                        {option.label}\n                      </SelectItem>\n                    ))}\n                  </SelectContent>\n                </Select>\n              </div>\n            </div>\n\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"location\">Location / Conference Link</Label>\n              <Input\n                id=\"location\"\n                name=\"location\"\n                placeholder=\"e.g., Conference Room B or https://meet.example.com/...\"\n              />\n            </div>\n\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"description\">Description</Label>\n              <Textarea\n                className=\"min-h-[100px]\"\n                id=\"description\"\n                name=\"description\"\n                placeholder=\"Optional: Add an agenda or notes...\"\n              />\n            </div>\n          </div>\n\n          <div className=\"flex items-center justify-end space-x-2 border-t p-4\">\n            <DialogClose render={<Button type=\"button\" variant=\"ghost\" />}>\n              Cancel\n            </DialogClose>\n            <Button size=\"sm\" type=\"submit\">\n              Schedule\n            </Button>\n          </div>\n        </form>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-11",
      "type": "registry:block",
      "title": "Dialog Multi-Step Wizard",
      "description": "A dialog multi-step wizard block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "dialog",
        "label",
        "select",
        "separator"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-11.tsx",
          "type": "registry:component",
          "target": "components/dialog-11.tsx",
          "content": "'use client';\n\nimport { AppWindowIcon as Apps } from 'lucide-react';\n\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Separator } from '@/components/ui/separator';\n\nexport default function Dialog11() {\n  return (\n    <Dialog defaultOpen>\n      <DialogTrigger render={<Button />}>Initialize New Project</DialogTrigger>\n      <DialogContent className=\"gap-0 overflow-visible p-0 sm:max-w-2xl\">\n        <DialogHeader className=\"mb-0 border-b px-6 py-4\">\n          <DialogTitle>Initialize New Project</DialogTitle>\n        </DialogHeader>\n\n        <form action=\"#\" method=\"POST\">\n          <div className=\"flex flex-col-reverse md:flex-row\">\n            <div className=\"flex flex-col justify-between md:w-80 md:border-r\">\n              <div className=\"flex-1 grow\">\n                <div className=\"border-t p-6 md:border-none\">\n                  <div className=\"flex items-center space-x-3\">\n                    <div className=\"inline-flex shrink-0 items-center justify-center rounded-sm bg-muted p-3\">\n                      <Apps\n                        aria-hidden={true}\n                        className=\"size-5 text-foreground\"\n                      />\n                    </div>\n                    <div className=\"space-y-0.5\">\n                      <h3 className=\"text-balance font-medium text-foreground text-sm\">\n                        Project Starter\n                      </h3>\n                      <p className=\"text-pretty text-muted-foreground text-sm\">\n                        Configure your new codebase\n                      </p>\n                    </div>\n                  </div>\n                  <Separator className=\"my-4\" />\n                  <h4 className=\"text-balance font-medium text-foreground text-sm\">\n                    Description\n                  </h4>\n                  <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6\">\n                    Quickly set up the foundational tools for your project.\n                  </p>\n                  <h4 className=\"mt-6 text-balance font-medium text-foreground text-sm\">\n                    Info\n                  </h4>\n                  <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6\">\n                    Select your preferred stack and configurations.\n                  </p>\n                </div>\n              </div>\n              <div className=\"flex items-center justify-between border-t p-4\">\n                <DialogClose render={<Button type=\"button\" variant=\"ghost\" />}>\n                  Cancel\n                </DialogClose>\n                <Button size=\"sm\" type=\"submit\">\n                  Initialize\n                </Button>\n              </div>\n            </div>\n\n            <div className=\"flex-1 space-y-6 p-6 md:px-6 md:pt-6 md:pb-8\">\n              <div className=\"space-y-2\">\n                <div className=\"flex items-center space-x-3\">\n                  <div className=\"inline-flex size-6 items-center justify-center rounded-sm bg-muted text-foreground text-sm\">\n                    1\n                  </div>\n                  <Label\n                    className=\"font-medium text-foreground text-sm\"\n                    htmlFor=\"framework\"\n                  >\n                    Select Framework\n                  </Label>\n                </div>\n                <Select\n                  defaultValue=\"react\"\n                  items={{\n                    react: 'Next.js',\n                    vue: 'React Router',\n                    angular: 'Tanstack Start',\n                    svelte: 'SvelteKit',\n                    vanilla: 'SolidStart',\n                  }}\n                >\n                  <SelectTrigger\n                    className=\"w-full\"\n                    id=\"framework\"\n                    name=\"framework\"\n                  >\n                    <SelectValue placeholder=\"Select framework\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"react\">Next.js</SelectItem>\n                    <SelectItem value=\"vue\">React Router</SelectItem>\n                    <SelectItem value=\"angular\">Tanstack Start</SelectItem>\n                    <SelectItem value=\"svelte\">SvelteKit</SelectItem>\n                    <SelectItem value=\"vanilla\">SolidStart</SelectItem>\n                  </SelectContent>\n                </Select>\n              </div>\n              <div className=\"space-y-2\">\n                <div className=\"flex items-center space-x-3\">\n                  <div className=\"inline-flex size-6 items-center justify-center rounded-sm bg-muted text-foreground text-sm\">\n                    2\n                  </div>\n                  <Label\n                    className=\"font-medium text-foreground text-sm\"\n                    htmlFor=\"package-manager\"\n                  >\n                    Choose Package Manager\n                  </Label>\n                </div>\n                <Select\n                  defaultValue=\"npm\"\n                  items={{ npm: 'npm', yarn: 'yarn', pnpm: 'pnpm', bun: 'bun' }}\n                >\n                  <SelectTrigger\n                    className=\"w-full\"\n                    id=\"package-manager\"\n                    name=\"package-manager\"\n                  >\n                    <SelectValue placeholder=\"Select manager\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"npm\">npm</SelectItem>\n                    <SelectItem value=\"yarn\">yarn</SelectItem>\n                    <SelectItem value=\"pnpm\">pnpm</SelectItem>\n                    <SelectItem value=\"bun\">bun</SelectItem>\n                  </SelectContent>\n                </Select>\n              </div>\n              <div>\n                <div className=\"flex items-center space-x-3\">\n                  <div className=\"inline-flex size-6 items-center justify-center rounded-sm bg-muted text-foreground text-sm\">\n                    3\n                  </div>\n                  <Label\n                    className=\"font-medium text-foreground text-sm\"\n                    htmlFor=\"linter\"\n                  >\n                    Configure Linter/Formatter\n                  </Label>\n                </div>\n                <p className=\"mt-1 text-pretty text-muted-foreground text-xs\">\n                  Ensure code quality and consistency.\n                </p>\n                <Select\n                  defaultValue=\"eslint-prettier\"\n                  items={{\n                    'eslint-prettier': 'ESLint + Prettier',\n                    eslint: 'ESLint Only',\n                    prettier: 'Prettier Only',\n                    biome: 'Biome',\n                    oxlint: 'Oxlint',\n                    none: 'None',\n                  }}\n                >\n                  <SelectTrigger\n                    className=\"mt-4 w-full\"\n                    id=\"linter\"\n                    name=\"linter\"\n                  >\n                    <SelectValue placeholder=\"Select tools\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"eslint-prettier\">\n                      ESLint + Prettier\n                    </SelectItem>\n                    <SelectItem value=\"eslint\">ESLint Only</SelectItem>\n                    <SelectItem value=\"prettier\">Prettier Only</SelectItem>\n                    <SelectItem value=\"biome\">Biome</SelectItem>\n                    <SelectItem value=\"oxlint\">Oxlint</SelectItem>\n                    <SelectItem value=\"none\">None</SelectItem>\n                  </SelectContent>\n                </Select>\n              </div>\n              <div>\n                <div className=\"flex items-center space-x-3\">\n                  <div className=\"inline-flex size-6 items-center justify-center rounded-sm bg-muted text-foreground text-sm\">\n                    4\n                  </div>\n                  <Label\n                    className=\"font-medium text-foreground text-sm\"\n                    htmlFor=\"testing-tool\"\n                  >\n                    Select Testing Tool\n                  </Label>\n                </div>\n                <p className=\"mt-1 text-pretty text-muted-foreground text-xs\">\n                  Choose a framework for unit/integration tests.\n                </p>\n                <Select\n                  defaultValue=\"jest\"\n                  items={{\n                    jest: 'Jest',\n                    vitest: 'Vitest',\n                    cypress: 'Cypress',\n                    none: 'None',\n                  }}\n                >\n                  <SelectTrigger\n                    className=\"mt-4 w-full\"\n                    id=\"testing-tool\"\n                    name=\"testing-tool\"\n                  >\n                    <SelectValue placeholder=\"Select tool\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"jest\">Jest</SelectItem>\n                    <SelectItem value=\"vitest\">Vitest</SelectItem>\n                    <SelectItem value=\"cypress\">Cypress</SelectItem>\n                    <SelectItem value=\"none\">None</SelectItem>\n                  </SelectContent>\n                </Select>\n              </div>\n            </div>\n          </div>\n        </form>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "dialog-12",
      "type": "registry:block",
      "title": "Dialog with Avatar Upload",
      "description": "A dialog with avatar upload block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "button",
        "dialog",
        "input",
        "label"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/dialogs/dialog-12.tsx",
          "type": "registry:component",
          "target": "components/dialog-12.tsx",
          "content": "'use client';\n\nimport { Plus, UserRoundIcon, X } from 'lucide-react';\nimport { useRef, useState } from 'react';\n\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Button } from '@/components/ui/button';\nimport {\n  Dialog,\n  DialogContent,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nexport default function Dialog12() {\n  const [open, setOpen] = useState(true);\n  const [authorName, setAuthorName] = useState('Ephraim Duncan');\n  const [title, setTitle] = useState('Design Engineer');\n  const [image, setImage] = useState<string | null>(null);\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const file = e.target.files?.[0];\n    if (file) {\n      if (file.size > 1_048_576) {\n        alert('File size exceeds 1MB limit');\n        return;\n      }\n\n      const reader = new FileReader();\n      reader.onload = (event) => {\n        setImage(event.target?.result as string);\n      };\n      reader.readAsDataURL(file);\n    }\n  };\n\n  const triggerFileInput = () => {\n    fileInputRef.current?.click();\n  };\n\n  return (\n    <Dialog onOpenChange={setOpen} open={open}>\n      <DialogTrigger render={<Button />}>Open Dialog</DialogTrigger>\n      <DialogContent className=\"gap-0 rounded-3xl p-0 sm:max-w-lg\">\n        <DialogHeader className=\"border-b px-6 py-4\">\n          <DialogTitle className=\"text-balance font-medium\">\n            Add a writer\n          </DialogTitle>\n        </DialogHeader>\n\n        <div className=\"grid grid-cols-1 px-6 pt-4 pb-6 md:grid-cols-5\">\n          <div className=\"flex flex-col items-center justify-center md:col-span-2\">\n            <div className=\"relative mb-2\">\n              <Avatar className=\"h-24 w-24 border-2 border-muted\">\n                <AvatarImage alt=\"Profile\" src={image || undefined} />\n                <AvatarFallback>\n                  <UserRoundIcon\n                    aria-hidden=\"true\"\n                    className=\"text-muted-foreground\"\n                    size={52}\n                  />\n                </AvatarFallback>\n              </Avatar>\n              <Button\n                className=\"-top-0.5 -right-0.5 absolute rounded-full border-[3px] border-background bg-accent hover:bg-accent\"\n                onClick={() => {\n                  if (image) {\n                    setImage(null);\n                    if (fileInputRef.current) {\n                      fileInputRef.current.value = '';\n                    }\n                  } else {\n                    triggerFileInput();\n                  }\n                }}\n                size=\"icon-sm\"\n                variant=\"ghost\"\n              >\n                {image ? (\n                  <X className=\"h-4 w-4 text-muted-foreground\" />\n                ) : (\n                  <Plus className=\"h-3 w-3 text-muted-foreground\" />\n                )}\n                <span className=\"sr-only\">\n                  {image ? 'Remove image' : 'Upload image'}\n                </span>\n              </Button>\n            </div>\n\n            <p className=\"text-pretty text-center font-medium\">Upload Image</p>\n            <p className=\"text-pretty text-center text-muted-foreground text-sm\">\n              Max file size: 1MB\n            </p>\n            <input\n              accept=\"image/*\"\n              className=\"hidden\"\n              onChange={handleFileChange}\n              ref={fileInputRef}\n              type=\"file\"\n            />\n            <Button\n              className=\"mt-2\"\n              onClick={triggerFileInput}\n              size=\"sm\"\n              variant=\"outline\"\n            >\n              Add Image\n            </Button>\n          </div>\n\n          <div className=\"flex flex-col justify-between md:col-span-3\">\n            <div className=\"space-y-4\">\n              <div className=\"space-y-1\">\n                <Label className=\"flex items-center\" htmlFor=\"author-name\">\n                  Author name <span className=\"text-primary\">*</span>\n                </Label>\n                <Input\n                  id=\"author-name\"\n                  onChange={(e) => setAuthorName(e.target.value)}\n                  required\n                  value={authorName}\n                />\n              </div>\n\n              <div className=\"space-y-1\">\n                <div className=\"flex items-center\">\n                  <Label htmlFor=\"title\">Title</Label>\n                </div>\n                <Input\n                  id=\"title\"\n                  onChange={(e) => setTitle(e.target.value)}\n                  value={title}\n                />\n              </div>\n            </div>\n\n            <div className=\"flex justify-end gap-2\">\n              <Button onClick={() => setOpen(false)} variant=\"outline\">\n                Cancel\n              </Button>\n              <Button className=\"bg-foreground text-background hover:bg-foreground/90\">\n                Save Changes\n              </Button>\n            </div>\n          </div>\n        </div>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
        }
      ],
      "categories": [
        "dialogs"
      ]
    },
    {
      "name": "file-upload-01",
      "type": "registry:block",
      "title": "File Upload with Preview",
      "description": "A file upload with preview block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "input",
        "label",
        "select",
        "tooltip"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/file-upload/file-upload-01/dropzone.tsx",
          "type": "registry:component",
          "target": "components/file-upload-01/dropzone.tsx",
          "content": "import { Upload } from 'lucide-react';\nimport type React from 'react';\nimport type { RefObject } from 'react';\n\ninterface FileDropzoneProps {\n  fileInputRef: RefObject<HTMLInputElement | null>;\n  handleBoxClick: () => void;\n  handleDragOver: (e: React.DragEvent) => void;\n  handleDrop: (e: React.DragEvent) => void;\n  handleFileSelect: (files: FileList | null) => void;\n}\n\nexport function FileDropzone({\n  fileInputRef,\n  handleBoxClick,\n  handleDragOver,\n  handleDrop,\n  handleFileSelect,\n}: FileDropzoneProps) {\n  return (\n    <div className=\"px-6\">\n      <div\n        className=\"flex cursor-pointer flex-col items-center justify-center rounded-md border-2 border-border border-dashed p-8 text-center\"\n        onClick={handleBoxClick}\n        onDragOver={handleDragOver}\n        onDrop={handleDrop}\n      >\n        <div className=\"mb-2 rounded-full bg-muted p-3\">\n          <Upload className=\"h-5 w-5 text-muted-foreground\" />\n        </div>\n        <p className=\"font-medium text-foreground text-sm\">\n          Upload a project image\n        </p>\n        <p className=\"mt-1 text-muted-foreground text-sm\">\n          or,{' '}\n          <label\n            className=\"cursor-pointer font-medium text-primary hover:text-primary/90\"\n            htmlFor=\"fileUpload\"\n            onClick={(e) => e.stopPropagation()} // Prevent triggering handleBoxClick\n          >\n            click to browse\n          </label>{' '}\n          (4MB max)\n        </p>\n        <input\n          accept=\"image/*\"\n          className=\"hidden\"\n          id=\"fileUpload\"\n          onChange={(e) => handleFileSelect(e.target.files)}\n          ref={fileInputRef}\n          type=\"file\"\n        />\n      </div>\n    </div>\n  );\n}\n"
        },
        {
          "path": "content/components/file-upload/file-upload-01/file-item.tsx",
          "type": "registry:component",
          "target": "components/file-upload-01/file-item.tsx",
          "content": "import { Trash2 } from 'lucide-react';\nimport { useEffect } from 'react';\nimport { Button } from '@/components/ui/button';\n\ninterface UploadedFileItemProps {\n  file: File;\n  progress: number;\n  onRemove: (filename: string) => void;\n}\n\nexport function UploadedFileItem({\n  file,\n  progress,\n  onRemove,\n}: UploadedFileItemProps) {\n  const imageUrl = URL.createObjectURL(file);\n\n  useEffect(() => {\n    return () => URL.revokeObjectURL(imageUrl);\n  }, [imageUrl]);\n\n  return (\n    <div\n      className=\"flex flex-col rounded-lg border border-border p-2\"\n      key={file.name}\n    >\n      <div className=\"flex items-center gap-2\">\n        <div className=\"row-span-2 flex h-14 w-18 items-center justify-center self-start overflow-hidden rounded-sm bg-muted\">\n          <img\n            alt={file.name}\n            className=\"h-full w-full object-cover\"\n            src={imageUrl}\n          />\n        </div>\n\n        <div className=\"flex-1 pr-1\">\n          <div className=\"flex items-center justify-between\">\n            <div className=\"flex items-center gap-2\">\n              <span className=\"max-w-[250px] truncate text-foreground text-sm\">\n                {file.name}\n              </span>\n              <span className=\"whitespace-nowrap text-muted-foreground text-sm\">\n                {Math.round(file.size / 1024)} KB\n              </span>\n            </div>\n            <Button\n              className=\"bg-transparent! hover:text-red-500\"\n              onClick={() => onRemove(file.name)}\n              size=\"icon-sm\"\n              variant=\"ghost\"\n            >\n              <Trash2 className=\"h-4 w-4\" />\n            </Button>\n          </div>\n\n          <div className=\"flex items-center gap-2\">\n            <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-muted\">\n              <div\n                className=\"h-full bg-primary\"\n                style={{\n                  width: `${progress || 0}%`,\n                }}\n              />\n            </div>\n            <span className=\"whitespace-nowrap text-muted-foreground text-xs\">\n              {Math.round(progress || 0)}%\n            </span>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        },
        {
          "path": "content/components/file-upload/file-upload-01/index.tsx",
          "type": "registry:component",
          "target": "components/file-upload-01/index.tsx",
          "content": "'use client';\n\nimport { HelpCircle } from 'lucide-react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent } from '@/components/ui/card';\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from '@/components/ui/tooltip';\nimport { FileDropzone } from '@/components/file-upload-01/dropzone';\nimport { FileList } from '@/components/file-upload-01/file-list';\nimport { Form } from '@/components/file-upload-01/form';\n\nexport default function FileUpload01() {\n  const fileInputRef = useRef<HTMLInputElement>(null);\n  const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);\n  const [fileProgresses, setFileProgresses] = useState<Record<string, number>>(\n    {}\n  );\n\n  const handleFileSelect = (files: FileList | null) => {\n    if (!files) return;\n\n    const newFiles = Array.from(files);\n    setUploadedFiles((prev) => [...prev, ...newFiles]);\n\n    newFiles.forEach((file) => {\n      let progress = 0;\n      const interval = setInterval(() => {\n        progress += Math.random() * 10;\n        if (progress >= 100) {\n          progress = 100;\n          clearInterval(interval);\n        }\n        setFileProgresses((prev) => ({\n          ...prev,\n          [file.name]: Math.min(progress, 100),\n        }));\n      }, 300);\n    });\n  };\n\n  const handleBoxClick = () => {\n    fileInputRef.current?.click();\n  };\n\n  const handleDragOver = (e: React.DragEvent) => {\n    e.preventDefault();\n  };\n\n  const handleDrop = (e: React.DragEvent) => {\n    e.preventDefault();\n    handleFileSelect(e.dataTransfer.files);\n  };\n\n  const removeFile = (filename: string) => {\n    setUploadedFiles((prev) => prev.filter((file) => file.name !== filename));\n    setFileProgresses((prev) => {\n      const newProgresses = { ...prev };\n      delete newProgresses[filename];\n      return newProgresses;\n    });\n  };\n\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <Card className=\"mx-auto w-full max-w-lg rounded-lg bg-background p-0 shadow-md\">\n        <CardContent className=\"p-0\">\n          <div className=\"p-6 pb-4\">\n            <div className=\"flex items-start justify-between\">\n              <div>\n                <h2 className=\"font-medium text-foreground text-lg\">\n                  Create a new project\n                </h2>\n                <p className=\"mt-1 text-muted-foreground text-sm\">\n                  Drag and drop files to create a new project.\n                </p>\n              </div>\n            </div>\n          </div>\n          <Form />\n          <FileDropzone\n            fileInputRef={fileInputRef}\n            handleBoxClick={handleBoxClick}\n            handleDragOver={handleDragOver}\n            handleDrop={handleDrop}\n            handleFileSelect={handleFileSelect}\n          />\n          <FileList\n            fileProgresses={fileProgresses}\n            removeFile={removeFile}\n            uploadedFiles={uploadedFiles}\n          />\n          <div className=\"flex items-center justify-between rounded-b-lg border-border border-t bg-muted px-6 py-3\">\n            <TooltipProvider delay={0}>\n              <Tooltip>\n                <TooltipTrigger\n                  render={\n                    <Button\n                      className=\"flex items-center text-muted-foreground hover:text-foreground\"\n                      size=\"sm\"\n                      variant=\"ghost\"\n                    />\n                  }\n                >\n                  <HelpCircle className=\"mr-1 h-4 w-4\" />\n                  Need help?\n                </TooltipTrigger>\n                <TooltipContent className=\"border bg-background py-3 text-foreground\">\n                  <div className=\"space-y-1\">\n                    <p className=\"font-medium text-[13px]\">Need assistance?</p>\n                    <p className=\"max-w-[200px] text-muted-foreground text-xs dark:text-muted-background\">\n                      Upload project images by dragging and dropping files or\n                      using the file browser. Supported formats: JPG, PNG, SVG.\n                      Maximum file size: 4MB.\n                    </p>\n                  </div>\n                </TooltipContent>\n              </Tooltip>\n            </TooltipProvider>\n\n            <div className=\"flex gap-2\">\n              <Button\n                className=\"h-9 px-4 font-medium text-sm\"\n                variant=\"outline\"\n              >\n                Cancel\n              </Button>\n              <Button className=\"h-9 px-4 font-medium text-sm\">Continue</Button>\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n    </div>\n  );\n}\n"
        },
        {
          "path": "content/components/file-upload/file-upload-01/form.tsx",
          "type": "registry:component",
          "target": "components/file-upload-01/form.tsx",
          "content": "import { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\n\nexport function Form() {\n  return (\n    <div className=\"mt-2 px-6 pb-4\">\n      <div className=\"grid grid-cols-2 gap-4\">\n        <div>\n          <Label className=\"mb-2\" htmlFor=\"projectName\">\n            Project name\n          </Label>\n          <Input\n            defaultValue=\"Open Source Stripe\"\n            id=\"projectName\"\n            type=\"text\"\n          />\n        </div>\n\n        <div>\n          <Label className=\"mb-2\" htmlFor=\"projectLead\">\n            Project lead\n          </Label>\n          <Select\n            defaultValue=\"1\"\n            items={{\n              '1': 'Ephraim Duncan',\n              '2': 'Lucas Smith',\n              '3': 'Timur Ercan',\n            }}\n          >\n            <SelectTrigger className=\"ps-2\" id=\"projectLead\">\n              <SelectValue placeholder=\"Select framework\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectGroup>\n                <SelectItem value=\"1\">\n                  <img\n                    alt=\"Ephraim Duncan\"\n                    className=\"size-5 rounded\"\n                    height={20}\n                    src=\"https://blocks.so/avatar-01.png\"\n                    width={20}\n                  />\n                  <span className=\"truncate\">Ephraim Duncan</span>\n                </SelectItem>\n                <SelectItem value=\"2\">\n                  <img\n                    alt=\"Lucas Smith\"\n                    className=\"size-5 rounded\"\n                    height={20}\n                    src=\"https://blocks.so/avatar-03.png\"\n                    width={20}\n                  />\n                  <span className=\"truncate\">Lucas Smith</span>\n                </SelectItem>\n                <SelectItem value=\"3\">\n                  <img\n                    alt=\"Timur Ercan\"\n                    className=\"size-5 rounded\"\n                    height={20}\n                    src=\"https://blocks.so/avatar-02.jpg\"\n                    width={20}\n                  />\n                  <span className=\"truncate\">Timur Ercan</span>\n                </SelectItem>\n              </SelectGroup>\n            </SelectContent>\n          </Select>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        },
        {
          "path": "content/components/file-upload/file-upload-01/file-list.tsx",
          "type": "registry:component",
          "target": "components/file-upload-01/file-list.tsx",
          "content": "import { cn } from '@/lib/utils';\nimport { UploadedFileItem } from '@/components/file-upload-01/file-item';\n\ninterface UploadedFileListProps {\n  uploadedFiles: File[];\n  fileProgresses: Record<string, number>;\n  removeFile: (filename: string) => void;\n}\n\nexport function FileList({\n  uploadedFiles,\n  fileProgresses,\n  removeFile,\n}: UploadedFileListProps) {\n  if (uploadedFiles.length === 0) {\n    return null;\n  }\n\n  return (\n    <div className={cn('mt-4 space-y-3 px-6 pb-5')}>\n      {uploadedFiles.map((file, index) => (\n        <UploadedFileItem\n          file={file}\n          key={file.name + index}\n          onRemove={removeFile}\n          progress={fileProgresses[file.name] || 0}\n        />\n      ))}\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "file-upload"
      ]
    },
    {
      "name": "file-upload-01",
      "type": "registry:block",
      "title": "File Upload with Preview",
      "description": "A file upload with preview block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "input",
        "label",
        "select",
        "tooltip"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/file-upload/file-upload-01.tsx",
          "type": "registry:component",
          "target": "components/file-upload-01.tsx",
          "content": "'use client';\n\nimport { HelpCircle, Trash2, Upload } from 'lucide-react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from '@/components/ui/tooltip';\nimport { cn } from '@/lib/utils';\n\nexport default function FileUpload01() {\n  const fileInputRef = useRef<HTMLInputElement>(null);\n  const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);\n  const [fileProgresses, setFileProgresses] = useState<Record<string, number>>(\n    {}\n  );\n\n  const handleFileSelect = (files: FileList | null) => {\n    if (!files) return;\n\n    const newFiles = Array.from(files);\n    setUploadedFiles((prev) => [...prev, ...newFiles]);\n\n    // Simulate upload progress for each file\n    newFiles.forEach((file) => {\n      let progress = 0;\n      const interval = setInterval(() => {\n        progress += Math.random() * 10;\n        if (progress >= 100) {\n          progress = 100;\n          clearInterval(interval);\n        }\n        setFileProgresses((prev) => ({\n          ...prev,\n          [file.name]: Math.min(progress, 100),\n        }));\n      }, 300);\n    });\n  };\n\n  const handleBoxClick = () => {\n    fileInputRef.current?.click();\n  };\n\n  const handleDragOver = (e: React.DragEvent) => {\n    e.preventDefault();\n  };\n\n  const handleDrop = (e: React.DragEvent) => {\n    e.preventDefault();\n    handleFileSelect(e.dataTransfer.files);\n  };\n\n  const removeFile = (filename: string) => {\n    setUploadedFiles((prev) => prev.filter((file) => file.name !== filename));\n    setFileProgresses((prev) => {\n      const newProgresses = { ...prev };\n      delete newProgresses[filename];\n      return newProgresses;\n    });\n  };\n\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <Card className=\"mx-auto w-full max-w-lg rounded-lg bg-background p-0 shadow-md\">\n        <CardContent className=\"p-0\">\n          <div className=\"p-6 pb-4\">\n            <div className=\"flex items-start justify-between\">\n              <div>\n                <h2 className=\"text-balance font-medium text-foreground text-lg\">\n                  Create a new project\n                </h2>\n                <p className=\"mt-1 text-pretty text-muted-foreground text-sm\">\n                  Drag and drop files to create a new project.\n                </p>\n              </div>\n            </div>\n          </div>\n\n          <div className=\"mt-2 px-6 pb-4\">\n            <div className=\"grid grid-cols-2 gap-4\">\n              <div>\n                <Label className=\"mb-2\" htmlFor=\"projectName\">\n                  Project name\n                </Label>\n                <Input\n                  defaultValue=\"Open Source Stripe\"\n                  id=\"projectName\"\n                  type=\"text\"\n                />\n              </div>\n\n              <div>\n                <Label className=\"mb-2\" htmlFor=\"projectLead\">\n                  Project lead\n                </Label>\n                <Select\n                  defaultValue=\"1\"\n                  items={{\n                    '1': 'Ephraim Duncan',\n                    '2': 'Lucas Smith',\n                    '3': 'Timur Ercan',\n                  }}\n                >\n                  <SelectTrigger className=\"w-full ps-2\" id=\"projectLead\">\n                    <SelectValue placeholder=\"Select project lead\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectGroup>\n                      <SelectItem value=\"1\">\n                        <img\n                          alt=\"Ephraim Duncan\"\n                          className=\"size-5 rounded\"\n                          height={20}\n                          src=\"https://blocks.so/avatar-01.png\"\n                          width={20}\n                        />\n                        <span className=\"truncate\">Ephraim Duncan</span>\n                      </SelectItem>\n                      <SelectItem value=\"2\">\n                        <img\n                          alt=\"Lucas Smith\"\n                          className=\"size-5 rounded\"\n                          height={20}\n                          src=\"https://blocks.so/avatar-03.png\"\n                          width={20}\n                        />\n                        <span className=\"truncate\">Lucas Smith</span>\n                      </SelectItem>\n                      <SelectItem value=\"3\">\n                        <img\n                          alt=\"Timur Ercan\"\n                          className=\"size-5 rounded\"\n                          height={20}\n                          src=\"https://blocks.so/avatar-02.jpg\"\n                          width={20}\n                        />\n                        <span className=\"truncate\">Timur Ercan</span>\n                      </SelectItem>\n                    </SelectGroup>\n                  </SelectContent>\n                </Select>\n              </div>\n            </div>\n          </div>\n\n          <div className=\"px-6\">\n            <div\n              className=\"flex cursor-pointer flex-col items-center justify-center rounded-md border-2 border-border border-dashed p-8 text-center\"\n              onClick={handleBoxClick}\n              onDragOver={handleDragOver}\n              onDrop={handleDrop}\n            >\n              <div className=\"mb-2 rounded-full bg-muted p-3\">\n                <Upload className=\"h-5 w-5 text-muted-foreground\" />\n              </div>\n              <p className=\"text-pretty font-medium text-foreground text-sm\">\n                Upload a project image\n              </p>\n              <p className=\"mt-1 text-pretty text-muted-foreground text-sm\">\n                or,{' '}\n                <label\n                  className=\"cursor-pointer font-medium text-primary hover:text-primary/90\"\n                  htmlFor=\"fileUpload\"\n                  onClick={(e) => e.stopPropagation()}\n                >\n                  click to browse\n                </label>{' '}\n                (4MB max)\n              </p>\n              <input\n                accept=\"image/*\"\n                className=\"hidden\"\n                id=\"fileUpload\"\n                onChange={(e) => handleFileSelect(e.target.files)}\n                ref={fileInputRef}\n                type=\"file\"\n              />\n            </div>\n          </div>\n\n          <div\n            className={cn(\n              'space-y-3 px-6 pb-5',\n              uploadedFiles.length > 0 ? 'mt-4' : ''\n            )}\n          >\n            {uploadedFiles.map((file, index) => {\n              const imageUrl = URL.createObjectURL(file);\n\n              return (\n                <div\n                  className=\"flex flex-col rounded-lg border border-border p-2\"\n                  key={file.name + index}\n                  onLoad={() => {\n                    return () => URL.revokeObjectURL(imageUrl);\n                  }}\n                >\n                  <div className=\"flex items-center gap-2\">\n                    <div className=\"row-span-2 flex h-14 w-18 items-center justify-center self-start overflow-hidden rounded-sm bg-muted\">\n                      <img\n                        alt={file.name}\n                        className=\"h-full w-full object-cover\"\n                        src={imageUrl}\n                      />\n                    </div>\n\n                    <div className=\"flex-1 pr-1\">\n                      <div className=\"flex items-center justify-between\">\n                        <div className=\"flex items-center gap-2\">\n                          <span className=\"max-w-[250px] truncate text-foreground text-sm\">\n                            {file.name}\n                          </span>\n                          <span className=\"whitespace-nowrap text-muted-foreground text-sm\">\n                            {Math.round(file.size / 1024)} KB\n                          </span>\n                        </div>\n                        <Button\n                          className=\"bg-transparent! hover:text-red-500\"\n                          onClick={() => removeFile(file.name)}\n                          size=\"icon-sm\"\n                          variant=\"ghost\"\n                        >\n                          <Trash2 className=\"h-4 w-4\" />\n                        </Button>\n                      </div>\n\n                      <div className=\"flex items-center gap-2\">\n                        <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-muted\">\n                          <div\n                            className=\"h-full bg-primary\"\n                            style={{\n                              width: `${fileProgresses[file.name] || 0}%`,\n                            }}\n                          />\n                        </div>\n                        <span className=\"whitespace-nowrap text-muted-foreground text-xs\">\n                          {Math.round(fileProgresses[file.name] || 0)}%\n                        </span>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              );\n            })}\n          </div>\n\n          <div className=\"flex items-center justify-between rounded-b-lg border-border border-t bg-muted px-6 py-3\">\n            <TooltipProvider delay={0}>\n              <Tooltip>\n                <TooltipTrigger\n                  render={\n                    <Button\n                      className=\"flex items-center text-muted-foreground hover:text-foreground\"\n                      size=\"sm\"\n                      variant=\"ghost\"\n                    />\n                  }\n                >\n                  <HelpCircle className=\"mr-1 h-4 w-4\" />\n                  Need help?\n                </TooltipTrigger>\n                <TooltipContent className=\"border bg-background py-3 text-foreground\">\n                  <div className=\"space-y-1\">\n                    <p className=\"text-pretty font-medium text-[13px]\">\n                      Need assistance?\n                    </p>\n                    <p className=\"max-w-[200px] text-pretty text-muted-foreground text-xs dark:text-muted-background\">\n                      Upload project images by dragging and dropping files or\n                      using the file browser. Supported formats: JPG, PNG, SVG.\n                      Maximum file size: 4MB.\n                    </p>\n                  </div>\n                </TooltipContent>\n              </Tooltip>\n            </TooltipProvider>\n\n            <div className=\"flex gap-2\">\n              <Button\n                className=\"h-9 px-4 font-medium text-sm\"\n                variant=\"outline\"\n              >\n                Cancel\n              </Button>\n              <Button className=\"h-9 px-4 font-medium text-sm\">Continue</Button>\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "file-upload"
      ]
    },
    {
      "name": "file-upload-02",
      "type": "registry:block",
      "title": "File Upload Simple",
      "description": "A file upload simple block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "input",
        "label"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/file-upload/file-upload-02.tsx",
          "type": "registry:component",
          "target": "components/file-upload-02.tsx",
          "content": "import { Button } from '@/components/ui/button';\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from '@/components/ui/card';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nexport default function FileUpload02() {\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <Card className=\"shadow-none\">\n        <CardHeader>\n          <CardTitle>Set up your first workspace</CardTitle>\n          <CardDescription>\n            Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n          </CardDescription>\n        </CardHeader>\n        <CardContent>\n          <form action=\"#\" method=\"POST\">\n            <div className=\"space-y-6\">\n              <div className=\"space-y-2\">\n                <Label htmlFor=\"workspace-name\">\n                  Workspace <span className=\"text-destructive\">*</span>\n                </Label>\n                <Input\n                  autoComplete=\"workspace-name\"\n                  id=\"workspace-name\"\n                  name=\"workspace-name\"\n                  placeholder=\"Workspace name\"\n                  required\n                  type=\"text\"\n                />\n              </div>\n              <div className=\"space-y-2\">\n                <Label htmlFor=\"file-1\">\n                  Upload file <span className=\"text-destructive\">*</span>\n                </Label>\n                <Input\n                  accept=\".csv, .xlsx, .xls\"\n                  id=\"file-1\"\n                  name=\"file-1\"\n                  type=\"file\"\n                />\n                <p className=\"text-pretty text-muted-foreground text-sm\">\n                  You are only allowed to upload CSV, XLSX or XLS files.\n                </p>\n              </div>\n            </div>\n            <div className=\"mt-8 flex justify-end space-x-3\">\n              <Button type=\"button\" variant=\"outline\">\n                Cancel\n              </Button>\n              <Button type=\"submit\">Submit</Button>\n            </div>\n          </form>\n        </CardContent>\n      </Card>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "file-upload"
      ]
    },
    {
      "name": "file-upload-03",
      "type": "registry:block",
      "title": "File Upload Multi-File Dropzone",
      "description": "A file upload multi-file dropzone block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "input",
        "label",
        "select",
        "separator"
      ],
      "dependencies": [
        "lucide-react",
        "react-dropzone"
      ],
      "files": [
        {
          "path": "content/components/file-upload/file-upload-03.tsx",
          "type": "registry:component",
          "target": "components/file-upload-03.tsx",
          "content": "'use client';\n\nimport { File, Trash } from 'lucide-react';\nimport React from 'react';\nimport { useDropzone } from 'react-dropzone';\n\nimport { Button } from '@/components/ui/button';\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from '@/components/ui/card';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Separator } from '@/components/ui/separator';\nimport { cn } from '@/lib/utils';\n\nexport default function FileUpload03() {\n  const [files, setFiles] = React.useState<File[]>([]);\n  const { getRootProps, getInputProps, isDragActive } = useDropzone({\n    onDrop: (acceptedFiles) => setFiles(acceptedFiles),\n  });\n\n  const filesList = files.map((file) => (\n    <li className=\"relative\" key={file.name}>\n      <Card className=\"relative p-4 shadow-none\">\n        <div className=\"-translate-y-1/2 absolute top-1/2 right-4\">\n          <Button\n            aria-label=\"Remove file\"\n            onClick={() =>\n              setFiles((prevFiles) =>\n                prevFiles.filter((prevFile) => prevFile.name !== file.name)\n              )\n            }\n            size=\"icon\"\n            type=\"button\"\n            variant=\"ghost\"\n          >\n            <Trash aria-hidden={true} className=\"h-5 w-5\" />\n          </Button>\n        </div>\n        <CardContent className=\"flex items-center space-x-3 p-0\">\n          <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-muted\">\n            <File aria-hidden={true} className=\"h-5 w-5 text-foreground\" />\n          </span>\n          <div>\n            <p className=\"text-pretty font-medium text-foreground\">\n              {file.name}\n            </p>\n            <p className=\"mt-0.5 text-pretty text-muted-foreground text-sm\">\n              {file.size} bytes\n            </p>\n          </div>\n        </CardContent>\n      </Card>\n    </li>\n  ));\n\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <Card className=\"shadow-none sm:mx-auto sm:max-w-xl\">\n        <CardHeader>\n          <CardTitle>Set up your first cloud storage</CardTitle>\n          <CardDescription>\n            Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n          </CardDescription>\n        </CardHeader>\n        <CardContent>\n          <form action=\"#\" method=\"post\">\n            <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-6\">\n              <div className=\"col-span-full sm:col-span-3\">\n                <Label className=\"font-medium\" htmlFor=\"bucket-name\">\n                  Bucket name\n                </Label>\n                <Input\n                  className=\"mt-2\"\n                  id=\"bucket-name\"\n                  name=\"bucket-name\"\n                  placeholder=\"Bucket name\"\n                  type=\"text\"\n                />\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Label className=\"font-medium\" htmlFor=\"visibility\">\n                  Visibility\n                </Label>\n                <Select\n                  defaultValue=\"private\"\n                  disabled\n                  items={{ private: 'Private', public: 'Public' }}\n                >\n                  <SelectTrigger\n                    className=\"mt-2 w-full\"\n                    id=\"visibility\"\n                    name=\"visibility\"\n                  >\n                    <SelectValue placeholder=\"Select visibility\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"private\">Private</SelectItem>\n                    <SelectItem value=\"public\">Public</SelectItem>\n                  </SelectContent>\n                </Select>\n                <p className=\"mt-2 text-pretty text-muted-foreground text-sm\">\n                  Only admins can change visibility.\n                </p>\n              </div>\n              <div className=\"col-span-full\">\n                <Label className=\"font-medium\" htmlFor=\"file-upload-2\">\n                  File(s) upload\n                </Label>\n                <div\n                  {...getRootProps()}\n                  className={cn(\n                    isDragActive\n                      ? 'border-primary bg-primary/10 ring-2 ring-primary/20'\n                      : 'border-border',\n                    'mt-2 flex justify-center rounded-md border border-dashed px-6 py-20 transition-colors duration-200'\n                  )}\n                >\n                  <div>\n                    <File\n                      aria-hidden={true}\n                      className=\"mx-auto h-12 w-12 text-muted-foreground/80\"\n                    />\n                    <div className=\"mt-4 flex text-muted-foreground\">\n                      <p>Drag and drop or</p>\n                      <label\n                        className=\"relative cursor-pointer rounded-sm pl-1 font-medium text-primary hover:text-primary/80 hover:underline hover:underline-offset-4\"\n                        htmlFor=\"file\"\n                      >\n                        <span>choose file(s)</span>\n                        <input\n                          {...getInputProps()}\n                          className=\"sr-only\"\n                          id=\"file-upload-2\"\n                          name=\"file-upload-2\"\n                          type=\"file\"\n                        />\n                      </label>\n                      <p className=\"text-pretty pl-1\">to upload</p>\n                    </div>\n                  </div>\n                </div>\n                <p className=\"mt-2 text-pretty text-muted-foreground text-sm leading-5 sm:flex sm:items-center sm:justify-between\">\n                  <span>All file types are allowed to upload.</span>\n                  <span className=\"pl-1 sm:pl-0\">Max. size per file: 50MB</span>\n                </p>\n                {filesList.length > 0 && (\n                  <>\n                    <h4 className=\"mt-6 text-balance font-medium text-foreground\">\n                      File(s) to upload\n                    </h4>\n                    <ul className=\"mt-4 space-y-4\" role=\"list\">\n                      {filesList}\n                    </ul>\n                  </>\n                )}\n              </div>\n            </div>\n            <Separator className=\"my-6\" />\n            <div className=\"flex items-center justify-end space-x-3\">\n              <Button type=\"button\" variant=\"outline\">\n                Cancel\n              </Button>\n              <Button type=\"submit\">Upload</Button>\n            </div>\n          </form>\n        </CardContent>\n      </Card>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "file-upload"
      ]
    },
    {
      "name": "file-upload-04",
      "type": "registry:block",
      "title": "File Upload Drag & Drop",
      "description": "A file upload drag & drop block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "progress"
      ],
      "dependencies": [
        "lucide-react",
        "sonner"
      ],
      "files": [
        {
          "path": "content/components/file-upload/file-upload-04.tsx",
          "type": "registry:component",
          "target": "components/file-upload-04.tsx",
          "content": "'use client';\n\nimport { File, FileSpreadsheet, X } from 'lucide-react';\nimport { type ChangeEvent, type DragEvent, useRef, useState } from 'react';\nimport { toast } from 'sonner';\n\nimport { Button } from '@/components/ui/button';\nimport { Card } from '@/components/ui/card';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function FileUpload04() {\n  const [uploadState, setUploadState] = useState<{\n    file: File | null;\n    progress: number;\n    uploading: boolean;\n  }>({\n    file: null,\n    progress: 0,\n    uploading: false,\n  });\n  const [showDummy, setShowDummy] = useState(true);\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  const validFileTypes = [\n    'text/csv',\n    'application/vnd.ms-excel',\n    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n  ];\n\n  const handleFile = (file: File | undefined) => {\n    if (!file) return;\n\n    if (validFileTypes.includes(file.type)) {\n      setUploadState({ file, progress: 0, uploading: true });\n\n      const interval = setInterval(() => {\n        setUploadState((prev) => {\n          const newProgress = prev.progress + 5;\n          if (newProgress >= 100) {\n            clearInterval(interval);\n            return { ...prev, progress: 100, uploading: false };\n          }\n          return { ...prev, progress: newProgress };\n        });\n      }, 200);\n    } else {\n      toast.error('Please upload a CSV, XLSX, or XLS file.', {\n        position: 'bottom-right',\n        duration: 3000,\n      });\n    }\n  };\n\n  const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {\n    handleFile(event.target.files?.[0]);\n  };\n\n  const handleDrop = (event: DragEvent<HTMLDivElement>) => {\n    event.preventDefault();\n    handleFile(event.dataTransfer.files?.[0]);\n  };\n\n  const resetFile = () => {\n    setUploadState({ file: null, progress: 0, uploading: false });\n    if (fileInputRef.current) {\n      fileInputRef.current.value = '';\n    }\n  };\n\n  const getFileIcon = () => {\n    if (!uploadState.file) return <File />;\n\n    const fileExt = uploadState.file.name.split('.').pop()?.toLowerCase() || '';\n    return ['csv', 'xlsx', 'xls'].includes(fileExt) ? (\n      <FileSpreadsheet className=\"h-5 w-5 text-foreground\" />\n    ) : (\n      <File className=\"h-5 w-5 text-foreground\" />\n    );\n  };\n\n  const formatFileSize = (bytes: number) => {\n    if (bytes === 0) return '0 Bytes';\n    const k = 1024;\n    const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n    const i = Math.floor(Math.log(bytes) / Math.log(k));\n    return Number.parseFloat((bytes / k ** i).toFixed(1)) + ' ' + sizes[i];\n  };\n\n  const { file, progress, uploading } = uploadState;\n\n  return (\n    <div className=\"flex w-full max-w-lg items-center justify-center p-10\">\n      <form className=\"w-full\" onSubmit={(e) => e.preventDefault()}>\n        <h3 className=\"text-balance font-semibold text-foreground text-lg\">\n          File Upload\n        </h3>\n\n        <div\n          className=\"mt-2 flex justify-center rounded-md border border-input border-dashed px-6 py-12\"\n          onDragOver={(e) => e.preventDefault()}\n          onDrop={handleDrop}\n        >\n          <div>\n            <File\n              aria-hidden={true}\n              className=\"mx-auto h-12 w-12 text-muted-foreground\"\n            />\n            <div className=\"flex text-muted-foreground text-sm leading-6\">\n              <p>Drag and drop or</p>\n              <label\n                className=\"relative cursor-pointer rounded-sm pl-1 font-medium text-primary hover:underline hover:underline-offset-4\"\n                htmlFor=\"file-upload-03\"\n              >\n                <span>choose file</span>\n                <input\n                  accept=\".csv, .xlsx, .xls\"\n                  className=\"sr-only\"\n                  id=\"file-upload-03\"\n                  name=\"file-upload-03\"\n                  onChange={handleFileChange}\n                  ref={fileInputRef}\n                  type=\"file\"\n                />\n              </label>\n              <p className=\"text-pretty pl-1\">to upload</p>\n            </div>\n          </div>\n        </div>\n\n        <p className=\"mt-2 text-pretty text-muted-foreground text-xs leading-5 sm:flex sm:items-center sm:justify-between\">\n          <span>Accepted file types: CSV, XLSX or XLS files.</span>\n          <span className=\"pl-1 sm:pl-0\">Max. size: 10MB</span>\n        </p>\n\n        {!file && showDummy && (\n          <Card className=\"relative mt-8 gap-4 bg-muted p-4 shadow-none\">\n            <Button\n              aria-label=\"Remove\"\n              className=\"absolute top-1 right-1 text-muted-foreground hover:text-foreground\"\n              onClick={() => setShowDummy(false)}\n              size=\"icon-sm\"\n              type=\"button\"\n              variant=\"ghost\"\n            >\n              <X aria-hidden={true} className=\"h-5 w-5 shrink-0\" />\n            </Button>\n\n            <div className=\"flex items-center space-x-2.5\">\n              <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-sm bg-background shadow-sm ring-1 ring-border ring-inset\">\n                <FileSpreadsheet\n                  aria-hidden={true}\n                  className=\"h-5 w-5 text-foreground\"\n                />\n              </span>\n              <div>\n                <p className=\"text-pretty font-medium text-foreground text-xs\">\n                  Revenue_Q1_2024.xlsx\n                </p>\n                <p className=\"mt-0.5 text-pretty text-muted-foreground text-xs\">\n                  3.1 MB\n                </p>\n              </div>\n            </div>\n\n            <div className=\"flex items-center space-x-3\">\n              <Progress className=\"h-1.5\" value={45} />\n              <span className=\"text-muted-foreground text-xs\">45%</span>\n            </div>\n          </Card>\n        )}\n\n        {file && (\n          <Card className=\"relative mt-8 gap-4 bg-muted p-4 shadow-none\">\n            <Button\n              aria-label=\"Remove\"\n              className=\"absolute top-1 right-1 text-muted-foreground hover:text-foreground\"\n              onClick={resetFile}\n              size=\"icon-sm\"\n              type=\"button\"\n              variant=\"ghost\"\n            >\n              <X aria-hidden={true} className=\"h-5 w-5 shrink-0\" />\n            </Button>\n\n            <div className=\"flex items-center space-x-2.5\">\n              <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-sm bg-background shadow-sm ring-1 ring-border ring-inset\">\n                {getFileIcon()}\n              </span>\n              <div>\n                <p className=\"text-pretty font-medium text-foreground text-xs\">\n                  {file?.name}\n                </p>\n                <p className=\"mt-0.5 text-pretty text-muted-foreground text-xs\">\n                  {file && formatFileSize(file.size)}\n                </p>\n              </div>\n            </div>\n\n            <div className=\"flex items-center space-x-3\">\n              <Progress className=\"h-1.5\" value={progress} />\n              <span className=\"text-muted-foreground text-xs\">{progress}%</span>\n            </div>\n          </Card>\n        )}\n\n        <div className=\"mt-8 flex items-center justify-end space-x-3\">\n          <Button\n            className=\"whitespace-nowrap\"\n            disabled={!file}\n            onClick={resetFile}\n            type=\"button\"\n            variant=\"outline\"\n          >\n            Cancel\n          </Button>\n          <Button\n            className=\"whitespace-nowrap\"\n            disabled={!file || uploading || progress < 100}\n            type=\"submit\"\n          >\n            Upload\n          </Button>\n        </div>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "file-upload"
      ]
    },
    {
      "name": "file-upload-05",
      "type": "registry:block",
      "title": "File Upload with Progress",
      "description": "A file upload with progress block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "label"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/file-upload/file-upload-05.tsx",
          "type": "registry:component",
          "target": "components/file-upload-05.tsx",
          "content": "import { FileSpreadsheet, Upload, X } from 'lucide-react';\nimport { Button } from '@/components/ui/button';\nimport { Label } from '@/components/ui/label';\n\nexport default function FileUpload05() {\n  return (\n    <div className=\"flex w-full max-w-lg items-center justify-center p-10 sm:mx-auto sm:max-w-lg\">\n      <form>\n        <h3 className=\"text-balance font-semibold text-foreground text-lg\">\n          File Upload\n        </h3>\n        <div className=\"mt-4 flex justify-center space-x-4 rounded-md border border-input border-dashed px-6 py-10\">\n          <div className=\"sm:flex sm:items-center sm:gap-x-3\">\n            <Upload\n              aria-hidden={true}\n              className=\"mx-auto h-8 w-8 text-muted-foreground sm:mx-0 sm:h-6 sm:w-6\"\n            />\n            <div className=\"mt-4 flex text-foreground text-sm leading-6 sm:mt-0\">\n              <p>Drag and drop or</p>\n              <Label\n                className=\"relative cursor-pointer rounded-sm pl-1 font-medium text-primary hover:underline hover:underline-offset-4\"\n                htmlFor=\"file-upload-4\"\n              >\n                <span>choose file</span>\n                <input\n                  className=\"sr-only\"\n                  id=\"file-upload-4\"\n                  name=\"file-upload-4\"\n                  type=\"file\"\n                />\n              </Label>\n              <p className=\"text-pretty pl-1\">to upload</p>\n            </div>\n          </div>\n        </div>\n        <p className=\"mt-2 flex items-center justify-between text-pretty text-muted-foreground text-xs leading-5\">\n          Recommended max. size: 10 MB, Accepted file types: XLSX, XLS, CSV.\n        </p>\n        <div className=\"relative mt-8 rounded-lg bg-muted p-3\">\n          <div className=\"absolute top-1 right-1\">\n            <Button\n              aria-label=\"Remove\"\n              className=\"rounded-sm p-2 text-muted-foreground hover:text-foreground\"\n              size=\"sm\"\n              type=\"button\"\n              variant=\"ghost\"\n            >\n              <X aria-hidden={true} className=\"size-4 shrink-0\" />\n            </Button>\n          </div>\n          <div className=\"flex items-center space-x-2.5\">\n            <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-sm bg-background shadow-sm ring-1 ring-input ring-inset\">\n              <FileSpreadsheet\n                aria-hidden={true}\n                className=\"size-5 text-foreground\"\n              />\n            </span>\n            <div className=\"w-full\">\n              <p className=\"text-pretty font-medium text-foreground text-xs\">\n                Revenue_Q1_2024.xlsx\n              </p>\n              <p className=\"mt-0.5 flex justify-between text-pretty text-muted-foreground text-xs\">\n                <span>3.1 MB</span>\n                <span>Completed</span>\n              </p>\n            </div>\n          </div>\n        </div>\n        <div className=\"mt-8 flex items-center justify-end space-x-3\">\n          <Button\n            className=\"whitespace-nowrap rounded-sm border border-input px-4 py-2 font-medium text-foreground text-sm shadow-sm hover:bg-accent hover:text-foreground\"\n            type=\"button\"\n            variant=\"outline\"\n          >\n            Cancel\n          </Button>\n          <Button\n            className=\"whitespace-nowrap rounded-sm bg-primary px-4 py-2 font-medium text-primary-foreground text-sm shadow-sm hover:bg-primary/90\"\n            type=\"submit\"\n            variant=\"default\"\n          >\n            Upload\n          </Button>\n        </div>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "file-upload"
      ]
    },
    {
      "name": "file-upload-06",
      "type": "registry:block",
      "title": "File Upload with Status Sections",
      "description": "A file upload with status sections block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "progress",
        "separator"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/file-upload/file-upload-06.tsx",
          "type": "registry:component",
          "target": "components/file-upload-06.tsx",
          "content": "'use client';\n\nimport { CheckCircle, FileText, Loader2, Upload, X } from 'lucide-react';\nimport type React from 'react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Card } from '@/components/ui/card';\nimport { Progress } from '@/components/ui/progress';\nimport { Separator } from '@/components/ui/separator';\n\ninterface UploadItem {\n  id: string;\n  name: string;\n  progress: number;\n  status: 'uploading' | 'completed';\n}\n\nexport default function FileUpload06() {\n  const [uploads, setUploads] = useState<UploadItem[]>([\n    {\n      id: 'a1',\n      name: 'design-mock-landing.png',\n      progress: 62,\n      status: 'uploading',\n    },\n    {\n      id: 'b2',\n      name: 'team-headshot-2025-01-09.jpg',\n      progress: 28,\n      status: 'uploading',\n    },\n    {\n      id: 'c3',\n      name: 'logo-v3-final.gif',\n      progress: 100,\n      status: 'completed',\n    },\n  ]);\n  const filePickerRef = useRef<HTMLInputElement>(null);\n\n  const openFilePicker = () => {\n    filePickerRef.current?.click();\n  };\n\n  const onFileInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n    const selectedFiles = event.target.files;\n    if (selectedFiles) {\n      console.log('Files selected:', selectedFiles);\n    }\n  };\n\n  const onDragOver = (event: React.DragEvent) => {\n    event.preventDefault();\n  };\n\n  const onDropFiles = (event: React.DragEvent) => {\n    event.preventDefault();\n    const droppedFiles = event.dataTransfer.files;\n    if (droppedFiles) {\n      console.log('Files dropped:', droppedFiles);\n    }\n  };\n\n  const removeUploadById = (id: string) => {\n    setUploads(uploads.filter((file) => file.id !== id));\n  };\n\n  const activeUploads = uploads.filter((file) => file.status === 'uploading');\n  const completedUploads = uploads.filter(\n    (file) => file.status === 'completed'\n  );\n\n  return (\n    <div className=\"mx-auto flex w-full max-w-sm flex-col gap-y-6\">\n      <Card\n        className=\"group flex max-h-[200px] w-full cursor-pointer flex-col items-center justify-center gap-4 border-dashed py-8 text-sm shadow-none transition-colors hover:bg-muted/50\"\n        onClick={openFilePicker}\n        onDragOver={onDragOver}\n        onDrop={onDropFiles}\n      >\n        <div className=\"grid space-y-3\">\n          <div className=\"flex items-center gap-x-2 text-muted-foreground\">\n            <Upload className=\"size-5\" />\n            <div>\n              Drop files here or{' '}\n              <Button\n                className=\"h-auto p-0 font-normal text-primary\"\n                onClick={openFilePicker}\n                variant=\"link\"\n              >\n                browse files\n              </Button>{' '}\n              to add\n            </div>\n          </div>\n        </div>\n        <input\n          accept=\"image/png,image/jpeg,image/gif\"\n          className=\"hidden\"\n          multiple\n          onChange={onFileInputChange}\n          ref={filePickerRef}\n          type=\"file\"\n        />\n        <span className=\"mt-2 block text-base/6 text-muted-foreground group-disabled:opacity-50 sm:text-xs\">\n          Supported: JPG, PNG, GIF (max 10 MB)\n        </span>\n      </Card>\n\n      <div className=\"flex flex-col gap-y-4\">\n        {activeUploads.length > 0 && (\n          <div>\n            <h2 className=\"mb-4 flex items-center text-balance font-mono font-normal text-foreground text-lg uppercase sm:text-xs\">\n              <Loader2 className=\"mr-1 size-4 animate-spin\" />\n              Uploading\n            </h2>\n            <div className=\"-mt-2 divide-y\">\n              {activeUploads.map((file) => (\n                <div className=\"group flex items-center py-4\" key={file.id}>\n                  <div className=\"mr-3 grid size-10 shrink-0 place-content-center rounded border bg-muted\">\n                    <FileText className=\"inline size-4 group-hover:hidden\" />\n                    <Button\n                      aria-label=\"Cancel\"\n                      className=\"hidden size-4 h-auto p-0 group-hover:inline\"\n                      onClick={() => removeUploadById(file.id)}\n                      size=\"icon\"\n                      variant=\"ghost\"\n                    >\n                      <X className=\"size-4\" />\n                    </Button>\n                  </div>\n                  <div className=\"mb-1 flex w-full flex-col\">\n                    <div className=\"flex justify-between gap-2\">\n                      <span className=\"select-none text-base/6 text-foreground group-disabled:opacity-50 sm:text-sm/6\">\n                        {file.name}\n                      </span>\n                      <span className=\"text-muted-foreground text-sm tabular-nums\">\n                        {file.progress}%\n                      </span>\n                    </div>\n                    <Progress\n                      className=\"mt-1 h-2 min-w-64\"\n                      value={file.progress}\n                    />\n                  </div>\n                </div>\n              ))}\n            </div>\n          </div>\n        )}\n\n        {activeUploads.length > 0 && completedUploads.length > 0 && (\n          <Separator className=\"my-0\" />\n        )}\n\n        {completedUploads.length > 0 && (\n          <div>\n            <h2 className=\"mb-4 flex items-center text-balance font-mono font-normal text-foreground text-lg uppercase sm:text-xs\">\n              <CheckCircle className=\"mr-1 size-4\" />\n              Finished\n            </h2>\n            <div className=\"-mt-2 divide-y\">\n              {completedUploads.map((file) => (\n                <div className=\"group flex items-center py-4\" key={file.id}>\n                  <div className=\"mr-3 grid size-10 shrink-0 place-content-center rounded border bg-muted\">\n                    <FileText className=\"inline size-4 group-hover:hidden\" />\n                    <Button\n                      aria-label=\"Remove\"\n                      className=\"hidden size-4 h-auto p-0 group-hover:inline\"\n                      onClick={() => removeUploadById(file.id)}\n                      size=\"icon\"\n                      variant=\"ghost\"\n                    >\n                      <X className=\"size-4\" />\n                    </Button>\n                  </div>\n                  <div className=\"mb-1 flex w-full flex-col\">\n                    <div className=\"flex justify-between gap-2\">\n                      <span className=\"select-none text-base/6 text-foreground group-disabled:opacity-50 sm:text-sm/6\">\n                        {file.name}\n                      </span>\n                      <span className=\"text-muted-foreground text-sm tabular-nums\">\n                        {file.progress}%\n                      </span>\n                    </div>\n                    <Progress\n                      className=\"mt-1 h-2 min-w-64\"\n                      value={file.progress}\n                    />\n                  </div>\n                </div>\n              ))}\n            </div>\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "file-upload"
      ]
    },
    {
      "name": "form-layout-01",
      "type": "registry:block",
      "title": "Form with Workspace Fields",
      "description": "A form with workspace fields block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "field",
        "input",
        "separator"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/form-layout/form-layout-01.tsx",
          "type": "registry:component",
          "target": "components/form-layout-01.tsx",
          "content": "import { Button } from '@/components/ui/button';\nimport { Field, FieldLabel } from '@/components/ui/field';\nimport { Input } from '@/components/ui/input';\nimport { Separator } from '@/components/ui/separator';\n\nexport default function FormLayout01() {\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <div className=\"sm:mx-auto sm:max-w-2xl\">\n        <h3 className=\"text-balance font-semibold text-2xl text-foreground dark:text-foreground\">\n          Register to workspace\n        </h3>\n        <p className=\"mt-1 text-pretty text-muted-foreground text-sm dark:text-muted-foreground\">\n          Take a few moments to register for your company&apos;s workspace\n        </p>\n        <form action=\"#\" className=\"mt-8\" method=\"post\">\n          <div className=\"grid grid-cols-1 gap-x-4 gap-y-6 sm:grid-cols-6\">\n            <div className=\"col-span-full sm:col-span-3\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"first-name\">\n                  First name\n                  <span className=\"text-red-500\">*</span>\n                </FieldLabel>\n                <Input\n                  autoComplete=\"first-name\"\n                  id=\"first-name\"\n                  name=\"first-name\"\n                  placeholder=\"First name\"\n                  required\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full sm:col-span-3\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"last-name\">\n                  Last name\n                  <span className=\"text-red-500\">*</span>\n                </FieldLabel>\n                <Input\n                  autoComplete=\"last-name\"\n                  id=\"last-name\"\n                  name=\"last-name\"\n                  placeholder=\"Last name\"\n                  required\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"email\">\n                  Email\n                  <span className=\"text-red-500\">*</span>\n                </FieldLabel>\n                <Input\n                  autoComplete=\"email\"\n                  id=\"email\"\n                  name=\"email\"\n                  placeholder=\"Email\"\n                  required\n                  type=\"email\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"address\">Address</FieldLabel>\n                <Input\n                  autoComplete=\"street-address\"\n                  id=\"address\"\n                  name=\"address\"\n                  placeholder=\"Address\"\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full sm:col-span-2\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"city\">City</FieldLabel>\n                <Input\n                  autoComplete=\"address-level2\"\n                  id=\"city\"\n                  name=\"city\"\n                  placeholder=\"City\"\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full sm:col-span-2\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"state\">State</FieldLabel>\n                <Input\n                  autoComplete=\"address-level1\"\n                  id=\"state\"\n                  name=\"state\"\n                  placeholder=\"State\"\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full sm:col-span-2\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"postal-code\">Postal code</FieldLabel>\n                <Input\n                  autoComplete=\"postal-code\"\n                  id=\"postal-code\"\n                  name=\"postal-code\"\n                  placeholder=\"Postal code\"\n                />\n              </Field>\n            </div>\n          </div>\n          <Separator className=\"my-6\" />\n          <div className=\"flex items-center justify-end space-x-4\">\n            <Button\n              className=\"whitespace-nowrap\"\n              type=\"button\"\n              variant=\"outline\"\n            >\n              Cancel\n            </Button>\n            <Button className=\"whitespace-nowrap\" type=\"submit\">\n              Submit\n            </Button>\n          </div>\n        </form>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "form-layout"
      ]
    },
    {
      "name": "form-layout-02",
      "type": "registry:block",
      "title": "Form with Side Labels",
      "description": "A form with side labels block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "field",
        "input",
        "radio-group",
        "select",
        "separator",
        "textarea"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/form-layout/form-layout-02.tsx",
          "type": "registry:component",
          "target": "components/form-layout-02.tsx",
          "content": "import { Button } from '@/components/ui/button';\nimport { Field, FieldDescription, FieldLabel } from '@/components/ui/field';\nimport { Input } from '@/components/ui/input';\nimport { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Separator } from '@/components/ui/separator';\nimport { Textarea } from '@/components/ui/textarea';\n\nexport default function FormLayout02() {\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <form>\n        <div className=\"grid grid-cols-1 gap-10 md:grid-cols-3\">\n          <div>\n            <h2 className=\"text-balance font-semibold text-foreground dark:text-foreground\">\n              Personal information\n            </h2>\n            <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6 dark:text-muted-foreground\">\n              Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n            </p>\n          </div>\n          <div className=\"sm:max-w-3xl md:col-span-2\">\n            <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-6\">\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"first-name\">First name</FieldLabel>\n                  <Input\n                    autoComplete=\"given-name\"\n                    id=\"first-name\"\n                    name=\"first-name\"\n                    placeholder=\"Emma\"\n                    type=\"text\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"last-name\">Last name</FieldLabel>\n                  <Input\n                    autoComplete=\"family-name\"\n                    id=\"last-name\"\n                    name=\"last-name\"\n                    placeholder=\"Crown\"\n                    type=\"text\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n                  <Input\n                    autoComplete=\"email\"\n                    id=\"email\"\n                    name=\"email\"\n                    placeholder=\"emma@company.com\"\n                    type=\"email\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"birthyear\">Birth year</FieldLabel>\n                  <Input\n                    id=\"birthyear\"\n                    name=\"year\"\n                    placeholder=\"1990\"\n                    type=\"number\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"role\">Role</FieldLabel>\n                  <Input\n                    disabled\n                    id=\"role\"\n                    name=\"role\"\n                    placeholder=\"Senior Manager\"\n                    type=\"text\"\n                  />\n                  <FieldDescription>\n                    Roles can only be changed by system admin.\n                  </FieldDescription>\n                </Field>\n              </div>\n            </div>\n          </div>\n        </div>\n        <Separator className=\"my-8\" />\n        <div className=\"grid grid-cols-1 gap-10 md:grid-cols-3\">\n          <div>\n            <h2 className=\"text-balance font-semibold text-foreground dark:text-foreground\">\n              Workspace settings\n            </h2>\n            <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6 dark:text-muted-foreground\">\n              Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n            </p>\n          </div>\n          <div className=\"sm:max-w-3xl md:col-span-2\">\n            <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-6\">\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"workspace-name\">\n                    Workspace name\n                  </FieldLabel>\n                  <Input\n                    id=\"workspace-name\"\n                    name=\"workspace-name\"\n                    placeholder=\"Test workspace\"\n                    type=\"text\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"visibility\">Visibility</FieldLabel>\n                  <Select\n                    defaultValue=\"private\"\n                    items={{ private: 'Private', public: 'Public' }}\n                    name=\"visibility\"\n                  >\n                    <SelectTrigger id=\"visibility\">\n                      <SelectValue placeholder=\"Select visibility\" />\n                    </SelectTrigger>\n                    <SelectContent>\n                      <SelectItem value=\"public\">Public</SelectItem>\n                      <SelectItem value=\"private\">Private</SelectItem>\n                    </SelectContent>\n                  </Select>\n                </Field>\n              </div>\n              <div className=\"col-span-full\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"workspace-description\">\n                    Workspace description\n                  </FieldLabel>\n                  <Textarea\n                    id=\"workspace-description\"\n                    name=\"workspace-description\"\n                    rows={4}\n                  />\n                  <FieldDescription>\n                    Note: description provided will not be displayed externally.\n                  </FieldDescription>\n                </Field>\n              </div>\n            </div>\n          </div>\n        </div>\n        <Separator className=\"my-8\" />\n        <div className=\"grid grid-cols-1 gap-10 md:grid-cols-3\">\n          <div>\n            <h2 className=\"text-balance font-semibold text-foreground dark:text-foreground\">\n              Notification settings\n            </h2>\n            <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6 dark:text-muted-foreground\">\n              Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n            </p>\n          </div>\n          <div className=\"sm:max-w-3xl md:col-span-2\">\n            <fieldset>\n              <legend className=\"font-medium text-foreground text-sm dark:text-foreground\">\n                Newsletter\n              </legend>\n              <FieldDescription\n                className=\"mt-2 leading-6\"\n                id=\"newsletter-description\"\n              >\n                Change how often you want to receive updates from our\n                newsletter.\n              </FieldDescription>\n              <RadioGroup className=\"mt-6\" defaultValue=\"never\">\n                <div className=\"flex items-center gap-x-3\">\n                  <RadioGroupItem\n                    aria-describedby=\"newsletter-description\"\n                    id=\"every-week\"\n                    value=\"every-week\"\n                  />\n                  <FieldLabel className=\"font-normal\" htmlFor=\"every-week\">\n                    Every week\n                  </FieldLabel>\n                </div>\n                <div className=\"flex items-center gap-x-3\">\n                  <RadioGroupItem\n                    aria-describedby=\"newsletter-description\"\n                    id=\"every-month\"\n                    value=\"every-month\"\n                  />\n                  <FieldLabel className=\"font-normal\" htmlFor=\"every-month\">\n                    Every month\n                  </FieldLabel>\n                </div>\n                <div className=\"flex items-center gap-x-3\">\n                  <RadioGroupItem\n                    aria-describedby=\"newsletter-description\"\n                    id=\"never\"\n                    value=\"never\"\n                  />\n                  <FieldLabel className=\"font-normal\" htmlFor=\"never\">\n                    Never\n                  </FieldLabel>\n                </div>\n              </RadioGroup>\n            </fieldset>\n          </div>\n        </div>\n        <Separator className=\"my-8\" />\n        <div className=\"flex items-center justify-end space-x-4\">\n          <Button className=\"whitespace-nowrap\" type=\"button\" variant=\"outline\">\n            Go back\n          </Button>\n          <Button className=\"whitespace-nowrap\" type=\"submit\">\n            Save settings\n          </Button>\n        </div>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "form-layout"
      ]
    },
    {
      "name": "form-layout-03",
      "type": "registry:block",
      "title": "Form with Checkbox Settings",
      "description": "A form with checkbox settings block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "checkbox",
        "field",
        "input",
        "select",
        "separator",
        "textarea"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/form-layout/form-layout-03.tsx",
          "type": "registry:component",
          "target": "components/form-layout-03.tsx",
          "content": "import { Button } from '@/components/ui/button';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Field, FieldDescription, FieldLabel } from '@/components/ui/field';\nimport { Input } from '@/components/ui/input';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Separator } from '@/components/ui/separator';\nimport { Textarea } from '@/components/ui/textarea';\n\nexport default function FormLayout03() {\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <form>\n        <div className=\"grid grid-cols-1 gap-10 md:grid-cols-3\">\n          <div>\n            <h2 className=\"text-balance font-semibold text-foreground dark:text-foreground\">\n              Personal information\n            </h2>\n            <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6 dark:text-muted-foreground\">\n              Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n            </p>\n          </div>\n          <div className=\"sm:max-w-3xl md:col-span-2\">\n            <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-6\">\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"first-name\">First name</FieldLabel>\n                  <Input\n                    autoComplete=\"given-name\"\n                    id=\"first-name\"\n                    name=\"first-name\"\n                    placeholder=\"Emma\"\n                    type=\"text\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"last-name\">Last name</FieldLabel>\n                  <Input\n                    autoComplete=\"family-name\"\n                    id=\"last-name\"\n                    name=\"last-name\"\n                    placeholder=\"Crown\"\n                    type=\"text\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n                  <Input\n                    autoComplete=\"email\"\n                    id=\"email\"\n                    name=\"email\"\n                    placeholder=\"emma@company.com\"\n                    type=\"email\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"birthyear\">Birth year</FieldLabel>\n                  <Input\n                    id=\"birthyear\"\n                    name=\"year\"\n                    placeholder=\"1990\"\n                    type=\"number\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"role\">Role</FieldLabel>\n                  <Input\n                    disabled\n                    id=\"role\"\n                    name=\"role\"\n                    placeholder=\"Senior Manager\"\n                    type=\"text\"\n                  />\n                  <FieldDescription>\n                    Roles can only be changed by system admin.\n                  </FieldDescription>\n                </Field>\n              </div>\n            </div>\n          </div>\n        </div>\n        <Separator className=\"my-8\" />\n        <div className=\"grid grid-cols-1 gap-10 md:grid-cols-3\">\n          <div>\n            <h2 className=\"text-balance font-semibold text-foreground dark:text-foreground\">\n              Workspace settings\n            </h2>\n            <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6 dark:text-muted-foreground\">\n              Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n            </p>\n          </div>\n          <div className=\"sm:max-w-3xl md:col-span-2\">\n            <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-6\">\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"workspace-name\">\n                    Workspace name\n                  </FieldLabel>\n                  <Input\n                    id=\"workspace-name\"\n                    name=\"workspace-name\"\n                    placeholder=\"Test workspace\"\n                    type=\"text\"\n                  />\n                </Field>\n              </div>\n              <div className=\"col-span-full sm:col-span-3\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"visibility\">Visibility</FieldLabel>\n                  <Select\n                    defaultValue=\"private\"\n                    items={{ private: 'Private', public: 'Public' }}\n                    name=\"visibility\"\n                  >\n                    <SelectTrigger id=\"visibility\">\n                      <SelectValue placeholder=\"Select visibility\" />\n                    </SelectTrigger>\n                    <SelectContent>\n                      <SelectItem value=\"public\">Public</SelectItem>\n                      <SelectItem value=\"private\">Private</SelectItem>\n                    </SelectContent>\n                  </Select>\n                </Field>\n              </div>\n              <div className=\"col-span-full\">\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"workspace-description\">\n                    Workspace description\n                  </FieldLabel>\n                  <Textarea\n                    id=\"workspace-description\"\n                    name=\"workspace-description\"\n                    rows={4}\n                  />\n                  <FieldDescription>\n                    Note: description provided will not be displayed externally.\n                  </FieldDescription>\n                </Field>\n              </div>\n            </div>\n          </div>\n        </div>\n        <Separator className=\"my-8\" />\n        <div className=\"grid grid-cols-1 gap-10 md:grid-cols-3\">\n          <div>\n            <h2 className=\"text-balance font-semibold text-foreground dark:text-foreground\">\n              Notification settings\n            </h2>\n            <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6 dark:text-muted-foreground\">\n              Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\n            </p>\n          </div>\n          <div className=\"sm:max-w-3xl md:col-span-2\">\n            <fieldset>\n              <legend className=\"font-medium text-foreground text-sm dark:text-foreground\">\n                Team\n              </legend>\n              <FieldDescription className=\"mt-1 leading-6\">\n                Configure the types of team alerts you want to receive.\n              </FieldDescription>\n              <div className=\"mt-2\">\n                <div className=\"flex items-center gap-x-3 py-1\">\n                  <Checkbox\n                    defaultChecked\n                    id=\"team-requests\"\n                    name=\"team-requests\"\n                  />\n                  <FieldLabel className=\"font-normal\" htmlFor=\"team-requests\">\n                    Team join requests\n                  </FieldLabel>\n                </div>\n                <div className=\"flex items-center gap-x-3 py-1\">\n                  <Checkbox\n                    id=\"team-activity-digest\"\n                    name=\"team-activity-digest\"\n                  />\n                  <FieldLabel\n                    className=\"font-normal\"\n                    htmlFor=\"team-activity-digest\"\n                  >\n                    Weekly team activity digest\n                  </FieldLabel>\n                </div>\n              </div>\n            </fieldset>\n            <fieldset className=\"mt-6\">\n              <legend className=\"font-medium text-foreground text-sm dark:text-foreground\">\n                Usage\n              </legend>\n              <FieldDescription className=\"mt-1 leading-6\">\n                Configure the types of usage alerts you want to receive.\n              </FieldDescription>\n              <div className=\"mt-2\">\n                <div className=\"flex items-center gap-x-3 py-1\">\n                  <Checkbox id=\"api-requests\" name=\"api-requests\" />\n                  <FieldLabel className=\"font-normal\" htmlFor=\"api-requests\">\n                    API requests\n                  </FieldLabel>\n                </div>\n                <div className=\"flex items-center gap-x-3 py-1\">\n                  <Checkbox\n                    id=\"workspace-execution\"\n                    name=\"workspace-execution\"\n                  />\n                  <FieldLabel\n                    className=\"font-normal\"\n                    htmlFor=\"workspace-execution\"\n                  >\n                    Workspace loading times\n                  </FieldLabel>\n                </div>\n                <div className=\"flex items-center gap-x-3 py-1\">\n                  <Checkbox\n                    defaultChecked\n                    id=\"query-caching\"\n                    name=\"query-caching\"\n                  />\n                  <FieldLabel className=\"font-normal\" htmlFor=\"query-caching\">\n                    Query caching\n                  </FieldLabel>\n                </div>\n                <div className=\"flex items-center gap-x-3 py-1\">\n                  <Checkbox defaultChecked id=\"storage\" name=\"storage\" />\n                  <FieldLabel className=\"font-normal\" htmlFor=\"storage\">\n                    Storage\n                  </FieldLabel>\n                </div>\n              </div>\n            </fieldset>\n          </div>\n        </div>\n        <Separator className=\"my-8\" />\n        <div className=\"flex items-center justify-end space-x-4\">\n          <Button className=\"whitespace-nowrap\" type=\"button\" variant=\"outline\">\n            Go back\n          </Button>\n          <Button className=\"whitespace-nowrap\" type=\"submit\">\n            Save settings\n          </Button>\n        </div>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "form-layout"
      ]
    },
    {
      "name": "form-layout-04",
      "type": "registry:block",
      "title": "Form with Package Selection",
      "description": "A form with package selection block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "field",
        "input",
        "radio-group",
        "select",
        "separator"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/form-layout/form-layout-04.tsx",
          "type": "registry:component",
          "target": "components/form-layout-04.tsx",
          "content": "'use client';\n\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Field, FieldDescription, FieldLabel } from '@/components/ui/field';\nimport { Input } from '@/components/ui/input';\nimport { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Separator } from '@/components/ui/separator';\n\nconst workspaces = [\n  {\n    id: 1,\n    title: 'Starter',\n    description: 'Up to 10,000 requests per day.',\n    users: 'Free',\n  },\n  {\n    id: 2,\n    title: 'Premium',\n    description: '500,000 requests per day¹',\n    users: '$900/month²',\n  },\n  {\n    id: 3,\n    title: 'Enterprise',\n    description: 'Based on your specific needs',\n    users: 'Custom',\n  },\n];\n\nexport default function FormLayout04() {\n  const [selectedWorkspace, setSelectedWorkspace] = useState(workspaces[0]);\n\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <div className=\"sm:mx-auto sm:max-w-2xl\">\n        <h3 className=\"text-balance font-semibold text-foreground text-lg\">\n          Apply for early access\n        </h3>\n        <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6\">\n          Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam\n          nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.\n        </p>\n        <form action=\"#\" className=\"mt-8\" method=\"post\">\n          <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-6\">\n            <div className=\"col-span-full sm:col-span-3\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"first-name\">\n                  First name<span className=\"text-red-500\">*</span>\n                </FieldLabel>\n                <Input\n                  autoComplete=\"given-name\"\n                  id=\"first-name\"\n                  name=\"first-name\"\n                  placeholder=\"Emma\"\n                  required\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full sm:col-span-3\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"last-name\">Last name</FieldLabel>\n                <Input\n                  autoComplete=\"family-name\"\n                  id=\"last-name\"\n                  name=\"last-name\"\n                  placeholder=\"Crown\"\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"email\">\n                  Work email<span className=\"text-red-500\">*</span>\n                </FieldLabel>\n                <Input\n                  autoComplete=\"email\"\n                  id=\"email\"\n                  name=\"email\"\n                  placeholder=\"emma@company.com\"\n                  required\n                  type=\"email\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full sm:col-span-3\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"company\">Company</FieldLabel>\n                <Input\n                  autoComplete=\"organization\"\n                  id=\"company\"\n                  name=\"company\"\n                  placeholder=\"Company, Inc.\"\n                  type=\"text\"\n                />\n              </Field>\n            </div>\n            <div className=\"col-span-full sm:col-span-3\">\n              <Field className=\"gap-2\">\n                <FieldLabel htmlFor=\"size\">Company size (employees)</FieldLabel>\n                <Select defaultValue=\"\">\n                  <SelectTrigger id=\"size\" name=\"size\">\n                    <SelectValue placeholder=\"Select company size\" />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"1-9\">1-9</SelectItem>\n                    <SelectItem value=\"10-50\">10-50</SelectItem>\n                    <SelectItem value=\"50-250\">50-250</SelectItem>\n                    <SelectItem value=\"250+\">250+</SelectItem>\n                  </SelectContent>\n                </Select>\n              </Field>\n            </div>\n            <Separator className=\"col-span-full my-4\" />\n            <div className=\"col-span-full\">\n              <FieldLabel className=\"mb-4 block font-semibold text-foreground\">\n                Select a workspace package\n              </FieldLabel>\n\n              <RadioGroup\n                className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\"\n                defaultValue={selectedWorkspace.id.toString()}\n                onValueChange={(value) =>\n                  setSelectedWorkspace(\n                    workspaces.find(\n                      (workspace) => workspace.id.toString() === value\n                    ) || workspaces[0]\n                  )\n                }\n              >\n                {workspaces.map((item) => (\n                  <div\n                    className=\"relative flex flex-col gap-2 rounded-md border border-input p-4 shadow-xs outline-none has-data-checked:border-ring\"\n                    key={item.id.toString()}\n                  >\n                    <div className=\"flex justify-between\">\n                      <RadioGroupItem\n                        className=\"order-1 after:absolute after:inset-0\"\n                        id={item.id.toString()}\n                        value={item.id.toString()}\n                      />\n\n                      <FieldLabel\n                        className=\"block font-medium text-foreground text-sm\"\n                        htmlFor={item.id.toString()}\n                      >\n                        {item.title}\n                      </FieldLabel>\n                    </div>\n                    <div className=\"flex h-full flex-col justify-between\">\n                      <p className=\"mt-1 text-pretty text-muted-foreground text-sm\">\n                        {item.description}\n                      </p>\n                      <span className=\"mt-4 block font-medium text-foreground text-sm\">\n                        {item.users}\n                      </span>\n                    </div>\n                  </div>\n                ))}\n              </RadioGroup>\n\n              <FieldDescription className=\"mt-2!\">\n                <sup>1</sup> $0.5/10K requests after limit reach.\n              </FieldDescription>\n              <FieldDescription className=\"mt-1\">\n                <sup>2</sup> No credit card required for registration.\n              </FieldDescription>\n            </div>\n          </div>\n          <Separator className=\"my-6\" />\n          <div className=\"flex items-center justify-end space-x-4\">\n            <Button\n              className=\"whitespace-nowrap\"\n              type=\"button\"\n              variant=\"outline\"\n            >\n              Go back\n            </Button>\n            <Button className=\"whitespace-nowrap\" type=\"submit\">\n              Apply\n            </Button>\n          </div>\n        </form>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "form-layout"
      ]
    },
    {
      "name": "form-layout-05",
      "type": "registry:block",
      "title": "Form with Plan Selection",
      "description": "A form with plan selection block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "button",
        "card",
        "field",
        "input",
        "radio-group",
        "select",
        "separator"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/form-layout/form-layout-05.tsx",
          "type": "registry:component",
          "target": "components/form-layout-05.tsx",
          "content": "'use client';\n\nimport { Check, CircleCheck, ExternalLink } from 'lucide-react';\nimport { useState } from 'react';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Field, FieldDescription, FieldLabel } from '@/components/ui/field';\nimport { Input } from '@/components/ui/input';\nimport { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport { Separator } from '@/components/ui/separator';\nimport { cn } from '@/lib/utils';\n\nconst highlights = [\n  {\n    id: 1,\n    feature: 'Used by top design teams worldwide',\n  },\n  {\n    id: 2,\n    feature: 'Seamless integration with design tools',\n  },\n  {\n    id: 3,\n    feature: 'Real-time collaboration features',\n  },\n];\n\nconst plans = [\n  {\n    name: 'Creator',\n    features: [\n      { feature: 'Up to 3 design projects' },\n      { feature: 'Basic collaboration tools' },\n      { feature: '5GB cloud storage' },\n      { feature: 'Community forum support' },\n    ],\n    price: '$15',\n    href: '#',\n    isRecommended: false,\n  },\n  {\n    name: 'Team',\n    features: [\n      { feature: 'Unlimited design projects' },\n      { feature: 'Advanced collaboration suite' },\n      { feature: '50GB cloud storage' },\n      { feature: 'Priority email support' },\n    ],\n    price: '$49',\n    href: '#',\n    isRecommended: true,\n  },\n  {\n    name: 'Agency',\n    features: [\n      { feature: 'Unlimited projects and team members' },\n      { feature: 'Client portal access' },\n      { feature: '250GB cloud storage' },\n      { feature: 'White-labeling options' },\n      { feature: 'Dedicated account manager' },\n    ],\n    price: '$99',\n    href: '#',\n    isRecommended: false,\n  },\n];\n\nexport default function FormLayout05() {\n  const [selected, setSelected] = useState(plans[0]);\n\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <form className=\"sm:mx-auto sm:max-w-7xl\">\n        <h3 className=\"text-balance font-semibold text-foreground text-xl\">\n          Create new design workspace\n        </h3>\n        <div className=\"grid grid-cols-1 gap-10 lg:grid-cols-12 lg:gap-12\">\n          <div className=\"mt-6 lg:col-span-7\">\n            <div className=\"space-y-4 md:space-y-6\">\n              <div className=\"md:flex md:items-center md:space-x-4\">\n                <div className=\"md:w-1/4\">\n                  <Field className=\"gap-2\">\n                    <FieldLabel htmlFor=\"organization\">Organization</FieldLabel>\n                    <Select\n                      defaultValue=\"1\"\n                      items={{\n                        '1': 'Acme, Inc.',\n                        '2': 'Hero Labs',\n                        '3': 'Rose Holding',\n                      }}\n                    >\n                      <SelectTrigger\n                        className=\"w-full\"\n                        id=\"organization\"\n                        name=\"organization\"\n                      >\n                        <SelectValue placeholder=\"Select organization\" />\n                      </SelectTrigger>\n                      <SelectContent>\n                        <SelectItem value=\"1\">Acme, Inc.</SelectItem>\n                        <SelectItem value=\"2\">Hero Labs</SelectItem>\n                        <SelectItem value=\"3\">Rose Holding</SelectItem>\n                      </SelectContent>\n                    </Select>\n                  </Field>\n                </div>\n                <div className=\"mt-4 md:mt-0 md:w-3/4\">\n                  <Field className=\"gap-2\">\n                    <FieldLabel htmlFor=\"workspace\">Workspace name</FieldLabel>\n                    <Input id=\"workspace\" name=\"workspace\" />\n                  </Field>\n                </div>\n              </div>\n              <div>\n                <Field className=\"gap-2\">\n                  <FieldLabel htmlFor=\"region\">Region</FieldLabel>\n                  <Select\n                    defaultValue=\"iad1\"\n                    items={{\n                      fra1: 'eu-central-1 (Frankfurt, Germany)',\n                      iad1: 'us-east-1 (Washington, D.C., USA)',\n                      lhr1: 'eu-west-2 (London, United Kingdom)',\n                      sfo1: 'us-west-1 (San Francisco, USA)',\n                      sin1: 'ap-southeast-1 (Singapore)',\n                    }}\n                  >\n                    <SelectTrigger id=\"region\" name=\"region\">\n                      <SelectValue placeholder=\"Select region\" />\n                    </SelectTrigger>\n                    <SelectContent>\n                      <SelectItem value=\"fra1\">\n                        eu-central-1 (Frankfurt, Germany)\n                      </SelectItem>\n                      <SelectItem value=\"iad1\">\n                        us-east-1 (Washington, D.C., USA)\n                      </SelectItem>\n                      <SelectItem value=\"lhr1\">\n                        eu-west-2 (London, United Kingdom)\n                      </SelectItem>\n                      <SelectItem value=\"sfo1\">\n                        us-west-1 (San Francisco, USA)\n                      </SelectItem>\n                      <SelectItem value=\"sin1\">\n                        ap-southeast-1 (Singapore)\n                      </SelectItem>\n                    </SelectContent>\n                  </Select>\n                  <FieldDescription>\n                    For best performance, choose a region closest to your\n                    operations\n                  </FieldDescription>\n                </Field>\n              </div>\n            </div>\n            <h4 className=\"mt-14 text-balance font-medium\">\n              Plan type<span className=\"text-red-500\">*</span>\n            </h4>\n            <RadioGroup\n              className=\"mt-4 space-y-4\"\n              onValueChange={(value) =>\n                setSelected(\n                  plans.find((plan) => plan.name === value) || plans[0]\n                )\n              }\n              value={selected.name}\n            >\n              {plans.map((plan) => (\n                <label\n                  className={cn(\n                    'relative block cursor-pointer rounded-md border bg-background transition',\n                    selected.name === plan.name\n                      ? 'border-primary/20 ring-2 ring-primary/20'\n                      : 'border-border'\n                  )}\n                  htmlFor={plan.name}\n                  key={plan.name}\n                >\n                  <div className=\"flex items-start space-x-4 px-6 py-4\">\n                    <div className=\"mt-1 flex h-4 w-4 shrink-0 items-center justify-center\">\n                      <RadioGroupItem id={plan.name} value={plan.name} />\n                    </div>\n                    <div className=\"w-full\">\n                      <p className=\"text-pretty leading-6\">\n                        <span className=\"font-semibold text-foreground\">\n                          {plan.name}\n                        </span>\n                        {plan.isRecommended && (\n                          <Badge className=\"ml-2\" variant=\"secondary\">\n                            recommended\n                          </Badge>\n                        )}\n                      </p>\n                      <ul className=\"mt-2 space-y-1\">\n                        {plan.features.map((feature, index) => (\n                          <li\n                            className=\"flex items-center gap-2 text-sm\"\n                            key={index}\n                          >\n                            <Check\n                              aria-hidden={true}\n                              className=\"h-4 w-4 text-muted-foreground\"\n                            />\n                            {feature.feature}\n                          </li>\n                        ))}\n                      </ul>\n                    </div>\n                  </div>\n                  <div className=\"flex items-center justify-between rounded-b-md border-border border-t bg-muted px-6 py-3\">\n                    <a\n                      className=\"inline-flex items-center gap-1 text-primary text-sm hover:underline hover:underline-offset-4\"\n                      href={plan.href}\n                    >\n                      Learn more\n                      <ExternalLink aria-hidden={true} className=\"h-4 w-4\" />\n                    </a>\n                    <div>\n                      <span className=\"font-semibold text-foreground text-lg\">\n                        {plan.price}\n                      </span>\n                      <span className=\"text-muted-foreground text-sm\">/mo</span>\n                    </div>\n                  </div>\n                </label>\n              ))}\n            </RadioGroup>\n          </div>\n          <div className=\"lg:col-span-5\">\n            <Card className=\"bg-muted shadow-none\">\n              <CardContent>\n                <h4 className=\"text-balance font-semibold text-foreground text-sm\">\n                  Choose the right plan for your design team\n                </h4>\n                <p className=\"mt-2 text-pretty text-muted-foreground text-sm leading-6\">\n                  Our flexible plans are designed to scale with your team&apos;s\n                  needs. All plans include core design collaboration features\n                  with varying levels of storage and support.\n                </p>\n                <ul className=\"mt-4 space-y-1\">\n                  {highlights.map((item) => (\n                    <li\n                      className=\"flex items-center space-x-2 py-1.5 text-foreground\"\n                      key={item.id}\n                    >\n                      <CircleCheck className=\"h-5 w-5 text-primary\" />\n                      <span className=\"truncate text-sm\">{item.feature}</span>\n                    </li>\n                  ))}\n                </ul>\n                <a\n                  className=\"mt-4 inline-flex items-center gap-1 text-primary text-sm\"\n                  href=\"#\"\n                >\n                  Learn more\n                  <ExternalLink aria-hidden={true} className=\"h-4 w-4\" />\n                </a>\n              </CardContent>\n            </Card>\n          </div>\n        </div>\n        <Separator className=\"my-10\" />\n        <div className=\"flex items-center justify-end space-x-4\">\n          <Button type=\"button\" variant=\"ghost\">\n            Cancel\n          </Button>\n          <Button type=\"submit\">Update</Button>\n        </div>\n      </form>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "form-layout"
      ]
    },
    {
      "name": "grid-list-01",
      "type": "registry:block",
      "title": "Grid List with Dropdown",
      "description": "A grid list with dropdown block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "dropdown-menu"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/grid-list/grid-list-01.tsx",
          "type": "registry:component",
          "target": "components/grid-list-01.tsx",
          "content": "import { MoreVertical } from 'lucide-react';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent } from '@/components/ui/card';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\n\nconst bookCollections = [\n  { name: 'Science Fiction', initials: 'SF', href: '#', books: 37 },\n  { name: 'Mystery Thrillers', initials: 'MT', href: '#', books: 29 },\n  { name: 'Historical Fiction', initials: 'HF', href: '#', books: 23 },\n];\n\nexport default function GridList01() {\n  return (\n    <div className=\"flex items-center justify-center p-8\">\n      <div>\n        <h2 className=\"text-balance font-medium text-muted-foreground text-sm\">\n          Favorite Book Collections\n        </h2>\n        <ul\n          className=\"mt-3 grid grid-cols-1 gap-5 sm:gap-6 lg:grid-cols-3\"\n          role=\"list\"\n        >\n          {bookCollections.map((collection) => (\n            <li className=\"col-span-1\" key={collection.name}>\n              <Card className=\"flex w-full flex-row gap-0 overflow-hidden rounded-md py-0 shadow-2xs\">\n                <div className=\"flex w-16 shrink-0 items-center justify-center bg-primary font-medium text-primary-foreground text-sm\">\n                  {collection.initials}\n                </div>\n                <CardContent className=\"flex flex-1 items-center justify-between truncate bg-card p-0\">\n                  <div className=\"flex-1 truncate px-4 py-2 text-sm\">\n                    <a\n                      className=\"font-medium text-foreground hover:text-muted-foreground\"\n                      href={collection.href}\n                    >\n                      {collection.name}\n                    </a>\n                    <p className=\"text-pretty text-muted-foreground\">\n                      {collection.books} Books\n                    </p>\n                  </div>\n                  <div className=\"shrink-0 pr-2\">\n                    <DropdownMenu>\n                      <DropdownMenuTrigger\n                        render={\n                          <Button\n                            className=\"h-8 w-8 rounded-full\"\n                            size=\"icon\"\n                            type=\"button\"\n                            variant=\"ghost\"\n                          />\n                        }\n                      >\n                        <span className=\"sr-only\">Open options</span>\n                        <MoreVertical className=\"h-5 w-5\" />\n                      </DropdownMenuTrigger>\n                      <DropdownMenuContent align=\"end\">\n                        <DropdownMenuItem>View collection</DropdownMenuItem>\n                        <DropdownMenuItem>Edit collection</DropdownMenuItem>\n                        <DropdownMenuSeparator />\n                        <DropdownMenuItem>Share collection</DropdownMenuItem>\n                        <DropdownMenuItem className=\"text-destructive hover:bg-destructive/10 hover:text-destructive focus:bg-destructive/10 focus:text-destructive\">\n                          Delete collection\n                        </DropdownMenuItem>\n                      </DropdownMenuContent>\n                    </DropdownMenu>\n                  </div>\n                </CardContent>\n              </Card>\n            </li>\n          ))}\n        </ul>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "grid-list"
      ]
    },
    {
      "name": "grid-list-02",
      "type": "registry:block",
      "title": "Grid List with Links",
      "description": "A grid list with links block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "card"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/grid-list/grid-list-02.tsx",
          "type": "registry:component",
          "target": "components/grid-list-02.tsx",
          "content": "import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Card, CardContent } from '@/components/ui/card';\n\nconst people = [\n  {\n    name: 'Timur Ercan',\n    email: 'timur@documenso.com',\n    role: 'Co-Founder / CEO',\n    imageUrl: 'https://blocks.so/avatar-02.png',\n  },\n  {\n    name: 'Lucas Smith',\n    email: 'lucas@documenso.com',\n    role: 'Co-Founder / CTO',\n    imageUrl: 'https://blocks.so/avatar-03.png',\n  },\n  {\n    name: 'Ephraim Duncan',\n    email: 'ephraim@documenso.com',\n    role: 'Software Engineer',\n    imageUrl: 'https://blocks.so/avatar-01.png',\n  },\n  {\n    name: 'Catalin Pit',\n    email: 'catalin@documenso.com',\n    role: 'Software Engineer',\n    imageUrl: 'https://blocks.so/avatar-04.png',\n  },\n];\n\nexport default function GridList02() {\n  return (\n    <div className=\"flex items-center justify-center p-8\">\n      <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n        {people.map((person) => (\n          <Card\n            className=\"relative border py-0 shadow-2xs transition-[border-color,box-shadow] duration-100 ease-out focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 hover:border-muted-foreground hover:shadow-sm\"\n            key={person.email}\n          >\n            <CardContent className=\"flex items-center space-x-4 p-4\">\n              <Avatar className=\"h-10 w-10\">\n                <AvatarImage alt={person.name} src={person.imageUrl} />\n                <AvatarFallback>{person.name.charAt(0)}</AvatarFallback>\n              </Avatar>\n              <div className=\"min-w-0 flex-1\">\n                <a className=\"focus:outline-none\" href=\"#\">\n                  <span aria-hidden=\"true\" className=\"absolute inset-0\" />\n                  <p className=\"text-pretty font-medium text-foreground text-sm\">\n                    {person.name}\n                  </p>\n                  <p className=\"truncate text-pretty text-muted-foreground text-sm\">\n                    {person.role}\n                  </p>\n                </a>\n              </div>\n            </CardContent>\n          </Card>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "grid-list"
      ]
    },
    {
      "name": "grid-list-03",
      "type": "registry:block",
      "title": "Grid List with Icons",
      "description": "A grid list with icons block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/grid-list/grid-list-03.tsx",
          "type": "registry:component",
          "target": "components/grid-list-03.tsx",
          "content": "import {\n  ArrowRight,\n  ArrowUpRight,\n  CheckCircle,\n  ContactRound,\n  Hand,\n  Server,\n  UserCircle,\n} from 'lucide-react';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\nconst actions = [\n  {\n    title: 'Getting Started',\n    description:\n      'Everything you need to know to get started and get to work in ChatCloud.',\n    href: '#',\n    icon: ArrowRight,\n    iconForeground: 'text-green-700',\n    iconBackground: 'bg-green-50 dark:bg-green-950/30',\n    ringColorClass: 'ring-green-700/30',\n  },\n  {\n    title: 'Admin Settings',\n    description:\n      'Learn how to manage your current workspace or your enterprise space.',\n    href: '#',\n    icon: UserCircle,\n    iconForeground: 'text-red-700',\n    iconBackground: 'bg-red-50 dark:bg-red-950/30',\n    ringColorClass: 'ring-red-700/30',\n  },\n  {\n    title: 'Server Setup',\n    description:\n      'Connect, simplify, and automate. Discover the power of apps and tools.',\n    href: '#',\n    icon: Server,\n    iconForeground: 'text-blue-700',\n    iconBackground: 'bg-blue-50 dark:bg-blue-950/30',\n    ringColorClass: 'ring-blue-700/30',\n  },\n  {\n    title: 'Login and Verification',\n    description:\n      'Read on to learn how to sign in with your email address, or your Apple or Google.',\n    href: '#',\n    icon: CheckCircle,\n    iconForeground: 'text-sky-700',\n    iconBackground: 'bg-sky-50 dark:bg-sky-950/30',\n    ringColorClass: 'ring-sky-700/30',\n  },\n  {\n    title: 'Account Setup',\n    description:\n      'Adjust your profile and preferences to make ChatCloud work just for you.',\n    href: '#',\n    icon: ContactRound,\n    iconForeground: 'text-pink-700',\n    iconBackground: 'bg-pink-50 dark:bg-pink-950/30',\n    ringColorClass: 'ring-pink-700/30',\n  },\n  {\n    title: 'Trust & Safety',\n    description:\n      'Trust on our current database and learn how we distribute your data.',\n    href: '#',\n    icon: Hand,\n    iconForeground: 'text-orange-700',\n    iconBackground: 'bg-orange-50 dark:bg-orange-950/30',\n    ringColorClass: 'ring-orange-700/30',\n  },\n];\n\nexport default function GridList03() {\n  return (\n    <div className=\"flex items-center justify-center p-8\">\n      <div className=\"grid grid-cols-1 space-y-0.5 overflow-hidden rounded-2xl bg-muted p-0.5 shadow-sm sm:grid-cols-2 sm:gap-0.5 sm:space-y-0 lg:grid-cols-3\">\n        {actions.map((action) => (\n          <Card\n            className={cn(\n              'group relative rounded-xl border-0 bg-card p-0 shadow-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-inset'\n            )}\n            key={action.title}\n          >\n            <CardContent className=\"p-6\">\n              <div>\n                <span\n                  className={cn(\n                    action.iconBackground,\n                    action.iconForeground,\n                    'inline-flex rounded-lg p-3 ring-2 ring-inset',\n                    action.ringColorClass\n                  )}\n                >\n                  <action.icon aria-hidden=\"true\" className=\"h-6 w-6\" />\n                </span>\n              </div>\n              <div className=\"mt-4\">\n                <h3 className=\"text-balance font-semibold text-base text-foreground\">\n                  <a className=\"focus:outline-none\" href={action.href}>\n                    <span aria-hidden=\"true\" className=\"absolute inset-0\" />\n                    {action.title}\n                  </a>\n                </h3>\n                <p className=\"mt-2 text-pretty text-muted-foreground text-sm\">\n                  {action.description}\n                </p>\n              </div>\n              <span\n                aria-hidden=\"true\"\n                className=\"pointer-events-none absolute top-6 right-6 text-muted-foreground/50 group-hover:text-muted-foreground/60\"\n              >\n                <ArrowUpRight className=\"h-6 w-6\" />\n              </span>\n            </CardContent>\n          </Card>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "grid-list"
      ]
    },
    {
      "name": "login-01",
      "type": "registry:block",
      "title": "Login with Email and Google",
      "description": "A login with email and google block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "input",
        "label",
        "separator"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/login/login-01.tsx",
          "type": "registry:component",
          "target": "components/login-01.tsx",
          "content": "import type { JSX, SVGProps } from 'react';\nimport { Button, buttonVariants } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Separator } from '@/components/ui/separator';\nimport { cn } from '@/lib/utils';\n\nconst GoogleIcon = (\n  props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>\n) => (\n  <svg fill=\"currentColor\" viewBox=\"0 0 24 24\" {...props}>\n    <path d=\"M3.06364 7.50914C4.70909 4.24092 8.09084 2 12 2C14.6954 2 16.959 2.99095 18.6909 4.60455L15.8227 7.47274C14.7864 6.48185 13.4681 5.97727 12 5.97727C9.39542 5.97727 7.19084 7.73637 6.40455 10.1C6.2045 10.7 6.09086 11.3409 6.09086 12C6.09086 12.6591 6.2045 13.3 6.40455 13.9C7.19084 16.2636 9.39542 18.0227 12 18.0227C13.3454 18.0227 14.4909 17.6682 15.3864 17.0682C16.4454 16.3591 17.15 15.3 17.3818 14.05H12V10.1818H21.4181C21.5364 10.8363 21.6 11.5182 21.6 12.2273C21.6 15.2727 20.5091 17.8363 18.6181 19.5773C16.9636 21.1046 14.7 22 12 22C8.09084 22 4.70909 19.7591 3.06364 16.4909C2.38638 15.1409 2 13.6136 2 12C2 10.3864 2.38638 8.85911 3.06364 7.50914Z\" />\n  </svg>\n);\n\nexport default function Login01() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <div className=\"flex flex-1 flex-col justify-center px-4 py-10 lg:px-6\">\n        <div className=\"sm:mx-auto sm:w-full sm:max-w-sm\">\n          <h2 className=\"text-balance text-center font-semibold text-foreground text-xl\">\n            Log in or create account\n          </h2>\n          <form action=\"#\" className=\"mt-6\" method=\"post\">\n            <Label className=\"font-medium text-foreground\" htmlFor=\"email\">\n              Email\n            </Label>\n            <Input\n              autoComplete=\"email\"\n              className=\"mt-2\"\n              id=\"email\"\n              name=\"email\"\n              placeholder=\"john@company.com\"\n              type=\"email\"\n            />\n            <Button className=\"mt-4 w-full\" type=\"submit\">\n              Sign in\n            </Button>\n          </form>\n\n          <div className=\"relative my-6\">\n            <div className=\"absolute inset-0 flex items-center\">\n              <Separator className=\"w-full\" />\n            </div>\n            <div className=\"relative flex justify-center text-xs uppercase\">\n              <span className=\"bg-background px-2 text-muted-foreground\">\n                or with\n              </span>\n            </div>\n          </div>\n\n          <a\n            className={cn(\n              buttonVariants({ variant: 'outline' }),\n              'inline-flex w-full items-center justify-center space-x-2'\n            )}\n            href=\"#\"\n          >\n            <GoogleIcon aria-hidden={true} className=\"size-5\" />\n            <span className=\"font-medium text-sm\">Sign in with Google</span>\n          </a>\n\n          <p className=\"mt-4 text-pretty text-muted-foreground text-xs\">\n            By signing in, you agree to our{' '}\n            <a className=\"underline underline-offset-4\" href=\"#\">\n              terms of service\n            </a>{' '}\n            and{' '}\n            <a className=\"underline underline-offset-4\" href=\"#\">\n              privacy policy\n            </a>\n            .\n          </p>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-02",
      "type": "registry:block",
      "title": "Login with Email, Password and Google",
      "description": "A login with email, password and google block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "input",
        "label",
        "separator"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/login/login-02.tsx",
          "type": "registry:component",
          "target": "components/login-02.tsx",
          "content": "import type { JSX, SVGProps } from 'react';\nimport { Button, buttonVariants } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Separator } from '@/components/ui/separator';\nimport { cn } from '@/lib/utils';\n\nconst GoogleIcon = (\n  props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>\n) => (\n  <svg fill=\"currentColor\" viewBox=\"0 0 24 24\" {...props}>\n    <path d=\"M3.06364 7.50914C4.70909 4.24092 8.09084 2 12 2C14.6954 2 16.959 2.99095 18.6909 4.60455L15.8227 7.47274C14.7864 6.48185 13.4681 5.97727 12 5.97727C9.39542 5.97727 7.19084 7.73637 6.40455 10.1C6.2045 10.7 6.09086 11.3409 6.09086 12C6.09086 12.6591 6.2045 13.3 6.40455 13.9C7.19084 16.2636 9.39542 18.0227 12 18.0227C13.3454 18.0227 14.4909 17.6682 15.3864 17.0682C16.4454 16.3591 17.15 15.3 17.3818 14.05H12V10.1818H21.4181C21.5364 10.8363 21.6 11.5182 21.6 12.2273C21.6 15.2727 20.5091 17.8363 18.6181 19.5773C16.9636 21.1046 14.7 22 12 22C8.09084 22 4.70909 19.7591 3.06364 16.4909C2.38638 15.1409 2 13.6136 2 12C2 10.3864 2.38638 8.85911 3.06364 7.50914Z\" />\n  </svg>\n);\n\nexport default function Login02() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <div className=\"flex flex-1 flex-col justify-center px-4 py-10 lg:px-6\">\n        <div className=\"sm:mx-auto sm:w-full sm:max-w-sm\">\n          <h2 className=\"text-balance text-center font-semibold text-foreground text-xl\">\n            Log in or create account\n          </h2>\n          <form action=\"#\" className=\"mt-6 space-y-4\" method=\"post\">\n            <div>\n              <Label\n                className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                htmlFor=\"email-login-02\"\n              >\n                Email\n              </Label>\n              <Input\n                autoComplete=\"email\"\n                className=\"mt-2\"\n                id=\"email-login-02\"\n                name=\"email-login-02\"\n                placeholder=\"ephraim@blocks.so\"\n                type=\"email\"\n              />\n            </div>\n            <div>\n              <Label\n                className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                htmlFor=\"password-login-02\"\n              >\n                Password\n              </Label>\n              <Input\n                autoComplete=\"password\"\n                className=\"mt-2\"\n                id=\"password-login-02\"\n                name=\"password-login-02\"\n                placeholder=\"**************\"\n                type=\"password\"\n              />\n            </div>\n            <Button className=\"mt-4 w-full py-2 font-medium\" type=\"submit\">\n              Sign in\n            </Button>\n          </form>\n\n          <div className=\"relative my-6\">\n            <div className=\"absolute inset-0 flex items-center\">\n              <Separator className=\"w-full\" />\n            </div>\n            <div className=\"relative flex justify-center text-xs uppercase\">\n              <span className=\"bg-background px-2 text-muted-foreground\">\n                or with\n              </span>\n            </div>\n          </div>\n\n          <a\n            className={cn(\n              buttonVariants({ variant: 'outline' }),\n              'flex w-full items-center justify-center space-x-2 py-2'\n            )}\n            href=\"#\"\n          >\n            <GoogleIcon aria-hidden={true} className=\"size-5\" />\n            <span className=\"font-medium text-sm\">Sign in with Google</span>\n          </a>\n\n          <p className=\"mt-4 text-pretty text-muted-foreground text-xs dark:text-muted-foreground\">\n            By signing in, you agree to our{' '}\n            <a className=\"underline underline-offset-4\" href=\"#\">\n              terms of service\n            </a>{' '}\n            and{' '}\n            <a className=\"underline underline-offset-4\" href=\"#\">\n              privacy policy\n            </a>\n            .\n          </p>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-03",
      "type": "registry:block",
      "title": "Login with Email and Password",
      "description": "A login with email and password block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "input",
        "label"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/login/login-03.tsx",
          "type": "registry:component",
          "target": "components/login-03.tsx",
          "content": "'use client';\n\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nexport default function Login03() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <div className=\"flex flex-1 flex-col justify-center px-4 py-10 lg:px-6\">\n        <div className=\"sm:mx-auto sm:w-full sm:max-w-sm\">\n          <h3 className=\"text-balance text-center font-semibold text-foreground text-lg dark:text-foreground\">\n            Welcome Back\n          </h3>\n          <p className=\"text-pretty text-center text-muted-foreground text-sm dark:text-muted-foreground\">\n            Enter your credentials to access your account.\n          </p>\n          <form action=\"#\" className=\"mt-6 space-y-4\" method=\"post\">\n            <div>\n              <Label\n                className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                htmlFor=\"email-login-03\"\n              >\n                Email\n              </Label>\n              <Input\n                autoComplete=\"email\"\n                className=\"mt-2\"\n                id=\"email-login-03\"\n                name=\"email-login-03\"\n                placeholder=\"ephraim@blocks.so\"\n                type=\"email\"\n              />\n            </div>\n            <div>\n              <Label\n                className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                htmlFor=\"password-login-03\"\n              >\n                Password\n              </Label>\n              <Input\n                autoComplete=\"password\"\n                className=\"mt-2\"\n                id=\"password-login-03\"\n                name=\"password-login-03\"\n                placeholder=\"**************\"\n                type=\"password\"\n              />\n            </div>\n            <Button className=\"mt-4 w-full py-2 font-medium\" type=\"submit\">\n              Sign in\n            </Button>\n          </form>\n          <p className=\"mt-6 text-pretty text-muted-foreground text-sm dark:text-muted-foreground\">\n            Forgot your password?{' '}\n            <a\n              className=\"font-medium text-primary hover:text-primary/90 dark:text-primary dark:hover:text-primary/90\"\n              href=\"#\"\n            >\n              Reset password\n            </a>\n          </p>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-04",
      "type": "registry:block",
      "title": "Login with Social Buttons",
      "description": "A login with social buttons block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "input",
        "label",
        "separator"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/login/login-04.tsx",
          "type": "registry:component",
          "target": "components/login-04.tsx",
          "content": "import type { JSX, SVGProps } from 'react';\nimport { Button, buttonVariants } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Separator } from '@/components/ui/separator';\nimport { cn } from '@/lib/utils';\n\nconst GitHubIcon = (\n  props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>\n) => (\n  <svg fill=\"currentColor\" viewBox=\"0 0 24 24\" {...props}>\n    <path d=\"M12.001 2C6.47598 2 2.00098 6.475 2.00098 12C2.00098 16.425 4.86348 20.1625 8.83848 21.4875C9.33848 21.575 9.52598 21.275 9.52598 21.0125C9.52598 20.775 9.51348 19.9875 9.51348 19.15C7.00098 19.6125 6.35098 18.5375 6.15098 17.975C6.03848 17.6875 5.55098 16.8 5.12598 16.5625C4.77598 16.375 4.27598 15.9125 5.11348 15.9C5.90098 15.8875 6.46348 16.625 6.65098 16.925C7.55098 18.4375 8.98848 18.0125 9.56348 17.75C9.65098 17.1 9.91348 16.6625 10.201 16.4125C7.97598 16.1625 5.65098 15.3 5.65098 11.475C5.65098 10.3875 6.03848 9.4875 6.67598 8.7875C6.57598 8.5375 6.22598 7.5125 6.77598 6.1375C6.77598 6.1375 7.61348 5.875 9.52598 7.1625C10.326 6.9375 11.176 6.825 12.026 6.825C12.876 6.825 13.726 6.9375 14.526 7.1625C16.4385 5.8625 17.276 6.1375 17.276 6.1375C17.826 7.5125 17.476 8.5375 17.376 8.7875C18.0135 9.4875 18.401 10.375 18.401 11.475C18.401 15.3125 16.0635 16.1625 13.8385 16.4125C14.201 16.725 14.5135 17.325 14.5135 18.2625C14.5135 19.6 14.501 20.675 14.501 21.0125C14.501 21.275 14.6885 21.5875 15.1885 21.4875C19.259 20.1133 21.9999 16.2963 22.001 12C22.001 6.475 17.526 2 12.001 2Z\" />\n  </svg>\n);\n\nconst GoogleIcon = (\n  props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>\n) => (\n  <svg fill=\"currentColor\" viewBox=\"0 0 24 24\" {...props}>\n    <path d=\"M3.06364 7.50914C4.70909 4.24092 8.09084 2 12 2C14.6954 2 16.959 2.99095 18.6909 4.60455L15.8227 7.47274C14.7864 6.48185 13.4681 5.97727 12 5.97727C9.39542 5.97727 7.19084 7.73637 6.40455 10.1C6.2045 10.7 6.09086 11.3409 6.09086 12C6.09086 12.6591 6.2045 13.3 6.40455 13.9C7.19084 16.2636 9.39542 18.0227 12 18.0227C13.3454 18.0227 14.4909 17.6682 15.3864 17.0682C16.4454 16.3591 17.15 15.3 17.3818 14.05H12V10.1818H21.4181C21.5364 10.8363 21.6 11.5182 21.6 12.2273C21.6 15.2727 20.5091 17.8363 18.6181 19.5773C16.9636 21.1046 14.7 22 12 22C8.09084 22 4.70909 19.7591 3.06364 16.4909C2.38638 15.1409 2 13.6136 2 12C2 10.3864 2.38638 8.85911 3.06364 7.50914Z\" />\n  </svg>\n);\n\nconst Logo = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n\nexport default function Login04() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <div className=\"flex flex-1 flex-col justify-center px-4 py-10 lg:px-6\">\n        <div className=\"sm:mx-auto sm:w-full sm:max-w-md\">\n          <div className=\"flex items-center space-x-1.5\">\n            <Logo\n              aria-hidden={true}\n              className=\"h-7 w-7 text-foreground dark:text-foreground\"\n            />\n            <p className=\"text-pretty font-medium text-foreground text-lg dark:text-foreground\">\n              Acme\n            </p>\n          </div>\n          <h3 className=\"mt-6 text-balance font-semibold text-foreground text-lg dark:text-foreground\">\n            Sign in to your account\n          </h3>\n          <p className=\"mt-2 text-pretty text-muted-foreground text-sm dark:text-muted-foreground\">\n            Don&apos;t have an account?{' '}\n            <a\n              className=\"font-medium text-primary hover:text-primary/90 dark:text-primary hover:dark:text-primary/90\"\n              href=\"#\"\n            >\n              Sign up\n            </a>\n          </p>\n          <div className=\"mt-8 flex flex-col items-center space-y-2 sm:flex-row sm:space-x-4 sm:space-y-0\">\n            <a\n              className={cn(\n                buttonVariants({ variant: 'outline' }),\n                'flex-1 items-center justify-center space-x-2 py-2'\n              )}\n              href=\"#\"\n            >\n              <GitHubIcon aria-hidden={true} className=\"size-5\" />\n              <span className=\"font-medium text-sm\">Login with GitHub</span>\n            </a>\n            <a\n              className={cn(\n                buttonVariants({ variant: 'outline' }),\n                'mt-2 flex-1 items-center justify-center space-x-2 py-2 sm:mt-0'\n              )}\n              href=\"#\"\n            >\n              <GoogleIcon aria-hidden={true} className=\"size-4\" />\n              <span className=\"font-medium text-sm\">Login with Google</span>\n            </a>\n          </div>\n\n          <div className=\"relative my-6\">\n            <div className=\"absolute inset-0 flex items-center\">\n              <Separator className=\"w-full\" />\n            </div>\n            <div className=\"relative flex justify-center text-xs uppercase\">\n              <span className=\"bg-background px-2 text-muted-foreground\">\n                or\n              </span>\n            </div>\n          </div>\n\n          <form action=\"#\" className=\"mt-6 space-y-4\" method=\"post\">\n            <div>\n              <Label\n                className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                htmlFor=\"email-login-04\"\n              >\n                Email\n              </Label>\n              <Input\n                autoComplete=\"email\"\n                className=\"mt-2\"\n                id=\"email-login-04\"\n                name=\"email-login-04\"\n                placeholder=\"ephraim@blocks.so\"\n                type=\"email\"\n              />\n            </div>\n            <div>\n              <Label\n                className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                htmlFor=\"password-login-04\"\n              >\n                Password\n              </Label>\n              <Input\n                autoComplete=\"password\"\n                className=\"mt-2\"\n                id=\"password-login-04\"\n                name=\"password-login-04\"\n                placeholder=\"********\"\n                type=\"password\"\n              />\n            </div>\n            <Button className=\"mt-4 w-full py-2 font-medium\" type=\"submit\">\n              Sign in\n            </Button>\n          </form>\n          <p className=\"mt-6 text-pretty text-muted-foreground text-sm dark:text-muted-foreground\">\n            Forgot your password?{' '}\n            <a\n              className=\"font-medium text-primary hover:text-primary/90 dark:text-primary hover:dark:text-primary/90\"\n              href=\"#\"\n            >\n              Reset password\n            </a>\n          </p>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-05",
      "type": "registry:block",
      "title": "Login Card Signup Form",
      "description": "A login card signup form block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "checkbox",
        "input",
        "label"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/login/login-05.tsx",
          "type": "registry:component",
          "target": "components/login-05.tsx",
          "content": "import type { JSX, SVGProps } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nconst Logo = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n\nexport default function Login05() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <div className=\"flex flex-1 flex-col justify-center px-4 py-10 lg:px-6\">\n        <div className=\"sm:mx-auto sm:w-full sm:max-w-md\">\n          <Logo\n            aria-hidden={true}\n            className=\"mx-auto h-10 w-10 text-foreground dark:text-foreground\"\n          />\n          <h3 className=\"mt-2 text-balance text-center font-bold text-foreground text-lg dark:text-foreground\">\n            Create new account for workspace\n          </h3>\n        </div>\n\n        <Card className=\"mt-4 shadow-2xs sm:mx-auto sm:w-full sm:max-w-md\">\n          <CardContent>\n            <form action=\"#\" className=\"space-y-4\" method=\"post\">\n              <div>\n                <Label\n                  className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                  htmlFor=\"name-login-05\"\n                >\n                  Name\n                </Label>\n                <Input\n                  autoComplete=\"name-login-05\"\n                  className=\"mt-2\"\n                  id=\"name-login-05\"\n                  name=\"name-login-05\"\n                  placeholder=\"Name\"\n                  type=\"text\"\n                />\n              </div>\n\n              <div>\n                <Label\n                  className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                  htmlFor=\"email-login-05\"\n                >\n                  Email\n                </Label>\n                <Input\n                  autoComplete=\"email-login-05\"\n                  className=\"mt-2\"\n                  id=\"email-login-05\"\n                  name=\"email-login-05\"\n                  placeholder=\"ephraim@blocks.so\"\n                  type=\"email\"\n                />\n              </div>\n\n              <div>\n                <Label\n                  className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                  htmlFor=\"password-login-05\"\n                >\n                  Password\n                </Label>\n                <Input\n                  autoComplete=\"password-login-05\"\n                  className=\"mt-2\"\n                  id=\"password-login-05   \"\n                  name=\"password-login-05\"\n                  placeholder=\"Password\"\n                  type=\"password\"\n                />\n              </div>\n\n              <div>\n                <Label\n                  className=\"font-medium text-foreground text-sm dark:text-foreground\"\n                  htmlFor=\"confirm-password-login-05\"\n                >\n                  Confirm password\n                </Label>\n                <Input\n                  autoComplete=\"confirm-password-login-05\"\n                  className=\"mt-2\"\n                  id=\"confirm-password-login-05\"\n                  name=\"confirm-password-login-05\"\n                  placeholder=\"Password\"\n                  type=\"password\"\n                />\n              </div>\n\n              <div className=\"mt-2 flex items-start\">\n                <div className=\"flex h-6 items-center\">\n                  <Checkbox\n                    className=\"size-4\"\n                    id=\"newsletter-login-05\"\n                    name=\"newsletter-login-05\"\n                  />\n                </div>\n                <Label\n                  className=\"ml-3 text-muted-foreground text-sm leading-6 dark:text-muted-foreground\"\n                  htmlFor=\"newsletter-login-05\"\n                >\n                  Sign up to our newsletter\n                </Label>\n              </div>\n\n              <Button className=\"mt-4 w-full py-2 font-medium\" type=\"submit\">\n                Create account\n              </Button>\n\n              <p className=\"text-pretty text-center text-muted-foreground text-xs dark:text-muted-foreground\">\n                By signing in, you agree to our{' '}\n                <a\n                  className=\"text-primary capitalize hover:text-primary/90 dark:text-primary hover:dark:text-primary/90\"\n                  href=\"#\"\n                >\n                  Terms of use\n                </a>{' '}\n                and{' '}\n                <a\n                  className=\"text-primary capitalize hover:text-primary/90 dark:text-primary hover:dark:text-primary/90\"\n                  href=\"#\"\n                >\n                  Privacy policy\n                </a>\n              </p>\n            </form>\n          </CardContent>\n        </Card>\n\n        <p className=\"mt-6 text-pretty text-center text-muted-foreground text-sm dark:text-muted-foreground\">\n          Already have an account?{' '}\n          <a\n            className=\"font-medium text-primary hover:text-primary/90 dark:text-primary hover:dark:text-primary/90\"\n            href=\"#\"\n          >\n            Sign in\n          </a>\n        </p>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-06",
      "type": "registry:block",
      "title": "Login with Magic Link and SSO",
      "description": "A login with magic link and sso block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "input",
        "separator"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/login/login-06.tsx",
          "type": "registry:component",
          "target": "components/login-06.tsx",
          "content": "import type { JSX, SVGProps } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Input } from '@/components/ui/input';\nimport { Separator } from '@/components/ui/separator';\n\nconst Logo = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n\nexport default function Login06() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <Card className=\"w-full max-w-sm rounded-4xl px-6 py-10 pt-14 shadow-2xs\">\n        <CardContent className=\"\">\n          <div className=\"flex flex-col items-center space-y-8\">\n            <Logo />\n\n            <div className=\"space-y-2 text-center\">\n              <h1 className=\"text-balance font-semibold text-3xl text-foreground\">\n                Welcome back!\n              </h1>\n              <p className=\"text-pretty text-muted-foreground text-sm\">\n                First time here?{' '}\n                <a className=\"text-foreground hover:underline\" href=\"#\">\n                  Sign up for free\n                </a>\n              </p>\n            </div>\n\n            <div className=\"w-full space-y-4\">\n              <Input\n                className=\"w-full rounded-xl\"\n                placeholder=\"Your email\"\n                type=\"email\"\n              />\n              <div className=\"flex flex-col gap-2\">\n                <Button className=\"w-full rounded-xl\" size=\"lg\">\n                  Send me the magic link\n                </Button>\n                <Button\n                  className=\"w-full text-muted-foreground text-sm\"\n                  variant=\"link\"\n                >\n                  Sign in using password\n                </Button>\n              </div>\n\n              <div className=\"flex items-center gap-4 py-2\">\n                <Separator className=\"flex-1\" />\n                <span className=\"text-muted-foreground text-sm\">OR</span>\n                <Separator className=\"flex-1\" />\n              </div>\n\n              <Button className=\"w-full rounded-xl\" size=\"lg\" variant=\"outline\">\n                Single sign-on (SSO)\n              </Button>\n            </div>\n\n            <p className=\"w-11/12 text-pretty text-center text-muted-foreground text-xs\">\n              You acknowledge that you read, and agree, to our{' '}\n              <a className=\"underline hover:text-foreground\" href=\"#\">\n                Terms of Service\n              </a>{' '}\n              and our{' '}\n              <a className=\"underline hover:text-foreground\" href=\"#\">\n                Privacy Policy\n              </a>\n              .\n            </p>\n          </div>\n        </CardContent>\n      </Card>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-07",
      "type": "registry:block",
      "title": "Login Full Page with Icons",
      "description": "A login full page with icons block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "checkbox",
        "input",
        "label",
        "separator"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/login/login-07.tsx",
          "type": "registry:component",
          "target": "components/login-07.tsx",
          "content": "'use client';\n\nimport { ArrowRight, Eye, EyeOff, Lock, Mail } from 'lucide-react';\nimport { type JSX, type SVGProps, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Separator } from '@/components/ui/separator';\n\nconst GoogleIcon = (\n  props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>\n) => (\n  <svg fill=\"currentColor\" viewBox=\"0 0 24 24\" {...props}>\n    <path d=\"M3.06364 7.50914C4.70909 4.24092 8.09084 2 12 2C14.6954 2 16.959 2.99095 18.6909 4.60455L15.8227 7.47274C14.7864 6.48185 13.4681 5.97727 12 5.97727C9.39542 5.97727 7.19084 7.73637 6.40455 10.1C6.2045 10.7 6.09086 11.3409 6.09086 12C6.09086 12.6591 6.2045 13.3 6.40455 13.9C7.19084 16.2636 9.39542 18.0227 12 18.0227C13.3454 18.0227 14.4909 17.6682 15.3864 17.0682C16.4454 16.3591 17.15 15.3 17.3818 14.05H12V10.1818H21.4181C21.5364 10.8363 21.6 11.5182 21.6 12.2273C21.6 15.2727 20.5091 17.8363 18.6181 19.5773C16.9636 21.1046 14.7 22 12 22C8.09084 22 4.70909 19.7591 3.06364 16.4909C2.38638 15.1409 2 13.6136 2 12C2 10.3864 2.38638 8.85911 3.06364 7.50914Z\" />\n  </svg>\n);\n\nconst Logo = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n\nexport default function Login07() {\n  const [isVisible, setIsVisible] = useState<boolean>(false);\n\n  const toggleVisibility = () => setIsVisible((prevState) => !prevState);\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <div className=\"mx-auto w-full max-w-xs space-y-6\">\n        <div className=\"space-y-2 text-center\">\n          <Logo className=\"mx-auto h-16 w-16\" />\n          <h1 className=\"text-balance font-semibold text-3xl\">Welcome back</h1>\n          <p className=\"text-pretty text-muted-foreground\">\n            Sign in to access to your dashboard, settings and projects.\n          </p>\n        </div>\n\n        <div className=\"space-y-5\">\n          <Button className=\"w-full justify-center gap-2\" variant=\"outline\">\n            <GoogleIcon className=\"h-4 w-4\" />\n            Sign in with Google\n          </Button>\n\n          <div className=\"flex items-center gap-2\">\n            <Separator className=\"flex-1\" />\n            <span className=\"text-muted-foreground text-sm\">\n              or sign in with email\n            </span>\n            <Separator className=\"flex-1\" />\n          </div>\n\n          <div className=\"space-y-6\">\n            <div>\n              <Label htmlFor=\"email\">Email</Label>\n              <div className=\"relative mt-2.5\">\n                <Input\n                  className=\"peer ps-9\"\n                  id=\"email\"\n                  placeholder=\"ephraim@blocks.so\"\n                  type=\"email\"\n                />\n                <div className=\"pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center ps-3 text-muted-foreground/80 peer-disabled:opacity-50\">\n                  <Mail aria-hidden=\"true\" size={16} />\n                </div>\n              </div>\n            </div>\n\n            <div>\n              <div className=\"flex items-center justify-between\">\n                <Label htmlFor=\"password\">Password</Label>\n                <a className=\"text-primary text-sm hover:underline\" href=\"#\">\n                  Forgot Password?\n                </a>\n              </div>\n              <div className=\"relative mt-2.5\">\n                <Input\n                  className=\"ps-9 pe-9\"\n                  id=\"password\"\n                  placeholder=\"Enter your password\"\n                  type={isVisible ? 'text' : 'password'}\n                />\n                <div className=\"pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center ps-3 text-muted-foreground/80 peer-disabled:opacity-50\">\n                  <Lock aria-hidden=\"true\" size={16} />\n                </div>\n                <button\n                  aria-controls=\"password\"\n                  aria-label={isVisible ? 'Hide password' : 'Show password'}\n                  aria-pressed={isVisible}\n                  className=\"absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-md text-muted-foreground/80 outline-none transition-[color,box-shadow] hover:text-foreground focus:z-10 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50\"\n                  onClick={toggleVisibility}\n                  type=\"button\"\n                >\n                  {isVisible ? (\n                    <EyeOff aria-hidden=\"true\" size={16} />\n                  ) : (\n                    <Eye aria-hidden=\"true\" size={16} />\n                  )}\n                </button>\n              </div>\n            </div>\n\n            <div className=\"flex items-center gap-2 pt-1\">\n              <Checkbox id=\"remember-me\" />\n              <Label htmlFor=\"remember-me\">Remember for 30 days</Label>\n            </div>\n          </div>\n\n          <Button className=\"w-full\">\n            Sign in\n            <ArrowRight className=\"h-4 w-4\" />\n          </Button>\n\n          <div className=\"text-center text-sm\">\n            No account?{' '}\n            <a className=\"font-medium text-primary hover:underline\" href=\"#\">\n              Create an account\n            </a>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-08",
      "type": "registry:block",
      "title": "Login with SSO",
      "description": "A login with sso block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "checkbox",
        "input",
        "label"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/login/login-08.tsx",
          "type": "registry:component",
          "target": "components/login-08.tsx",
          "content": "'use client';\n\nimport { EyeIcon, EyeOffIcon, Key } from 'lucide-react';\nimport Link from 'next/link';\nimport { type JSX, type SVGProps, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Card,\n  CardContent,\n  CardFooter,\n  CardHeader,\n} from '@/components/ui/card';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\nconst Logo = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n\nexport default function SignIn() {\n  const [isPasswordVisible, setIsPasswordVisible] = useState<boolean>(false);\n\n  const togglePasswordVisibility = () => setIsPasswordVisible((prev) => !prev);\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <Card className=\"mx-4 w-full max-w-md pb-0 shadow-2xs\">\n        <CardHeader className=\"mt-4 mb-2 space-y-1 text-center\">\n          <div className=\"flex justify-center\">\n            <Logo />\n          </div>\n          <div>\n            <h2 className=\"text-balance font-semibold text-2xl\">\n              Sign in to Acme\n            </h2>\n            <p className=\"text-pretty text-muted-foreground text-sm\">\n              Welcome back! Please enter your details.\n            </p>\n          </div>\n        </CardHeader>\n        <CardContent className=\"space-y-6\">\n          <div className=\"space-y-2\">\n            <Label htmlFor=\"email\">Email address</Label>\n            <Input id=\"email\" placeholder=\"ephraim@blocks.so\" type=\"email\" />\n          </div>\n          <div className=\"space-y-0\">\n            <div className=\"mb-2 flex items-center justify-between\">\n              <Label htmlFor=\"password\">Password</Label>\n              <Link className=\"text-primary text-sm hover:underline\" href=\"#\">\n                Reset password\n              </Link>\n            </div>\n            <div className=\"relative\">\n              <Input\n                className=\"pe-9\"\n                id=\"password\"\n                placeholder=\"Enter your password\"\n                type={isPasswordVisible ? 'text' : 'password'}\n              />\n              <button\n                aria-controls=\"password\"\n                aria-label={\n                  isPasswordVisible ? 'Hide password' : 'Show password'\n                }\n                aria-pressed={isPasswordVisible}\n                className=\"absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-md text-muted-foreground/80 outline-none transition-[color,box-shadow] hover:text-foreground focus:z-10 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50\"\n                onClick={togglePasswordVisibility}\n                type=\"button\"\n              >\n                {isPasswordVisible ? (\n                  <EyeOffIcon aria-hidden=\"true\" size={16} />\n                ) : (\n                  <EyeIcon aria-hidden=\"true\" size={16} />\n                )}\n              </button>\n            </div>\n          </div>\n          <div className=\"flex items-center space-x-2\">\n            <Checkbox defaultChecked id=\"remember\" />\n            <Label className=\"font-normal text-sm\" htmlFor=\"remember\">\n              Remember me\n            </Label>\n          </div>\n\n          <div className=\"space-y-2\">\n            <Button className=\"w-full\" type=\"submit\">\n              Sign In\n            </Button>\n            <Button className=\"w-full\" type=\"button\" variant=\"outline\">\n              <Key className=\"mr-2 h-4 w-4\" />\n              Single sign-on (SSO)\n            </Button>\n          </div>\n        </CardContent>\n        <CardFooter className=\"flex justify-center border-t py-4!\">\n          <p className=\"text-pretty text-center text-muted-foreground text-sm\">\n            New to Acme?{' '}\n            <Link className=\"text-primary hover:underline\" href=\"#\">\n              Sign up\n            </Link>\n          </p>\n        </CardFooter>\n      </Card>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "login-09",
      "type": "registry:block",
      "title": "Login Signup with Role Selection",
      "description": "A login signup with role selection block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "checkbox",
        "input",
        "label",
        "select"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/login/login-09.tsx",
          "type": "registry:component",
          "target": "components/login-09.tsx",
          "content": "'use client';\n\nimport { BarChart, Code, Eye, EyeOff, User } from 'lucide-react';\nimport Link from 'next/link';\nimport { type JSX, type SVGProps, useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  Card,\n  CardContent,\n  CardFooter,\n  CardHeader,\n} from '@/components/ui/card';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\n\nconst Logo = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n\nexport default function SignupForm() {\n  const [showPassword, setShowPassword] = useState(false);\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center\">\n      <div className=\"w-full max-w-md\">\n        <Card className=\"border-none pb-0 shadow-lg\">\n          <CardHeader className=\"flex flex-col items-center space-y-1.5 pt-6 pb-4\">\n            <Logo className=\"h-12 w-12\" />\n            <div className=\"flex flex-col items-center space-y-0.5\">\n              <h2 className=\"text-balance font-semibold text-2xl text-foreground\">\n                Create an account\n              </h2>\n              <p className=\"text-pretty text-muted-foreground\">\n                Welcome! Create an account to get started.\n              </p>\n            </div>\n          </CardHeader>\n          <CardContent className=\"space-y-6 px-8\">\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"role\">Role</Label>\n              <Select\n                defaultValue=\"designer\"\n                items={{\n                  designer: 'Product Designer',\n                  developer: 'Developer',\n                  manager: 'Product Manager',\n                }}\n              >\n                <SelectTrigger\n                  className=\"[&>span]:flex [&>span]:items-center [&>span]:gap-2 [&>span_svg]:shrink-0\"\n                  id=\"role\"\n                >\n                  <SelectValue placeholder=\"Select role\" />\n                </SelectTrigger>\n                <SelectContent className=\"[&_*[role=option]>span>svg]:shrink-0 [&_*[role=option]>span]:start-auto [&_*[role=option]>span]:end-2 [&_*[role=option]>span]:flex [&_*[role=option]>span]:items-center [&_*[role=option]>span]:gap-2 [&_*[role=option]]:ps-2 [&_*[role=option]]:pe-8\">\n                  <SelectItem value=\"designer\">\n                    <User aria-hidden=\"true\" size={16} />\n                    <span className=\"truncate\">Product Designer</span>\n                  </SelectItem>\n                  <SelectItem value=\"developer\">\n                    <Code aria-hidden=\"true\" size={16} />\n                    <span className=\"truncate\">Developer</span>\n                  </SelectItem>\n                  <SelectItem value=\"manager\">\n                    <BarChart aria-hidden=\"true\" size={16} />\n                    <span className=\"truncate\">Product Manager</span>\n                  </SelectItem>\n                </SelectContent>\n              </Select>\n            </div>\n\n            <div className=\"grid grid-cols-2 gap-4\">\n              <div className=\"space-y-2\">\n                <Label htmlFor=\"firstName\">First name</Label>\n                <Input id=\"firstName\" />\n              </div>\n              <div className=\"space-y-2\">\n                <Label htmlFor=\"lastName\">Last name</Label>\n                <Input id=\"lastName\" />\n              </div>\n            </div>\n\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"username\">Username</Label>\n              <Input id=\"username\" />\n            </div>\n\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"email\">Email address</Label>\n              <Input id=\"email\" type=\"email\" />\n            </div>\n\n            <div className=\"space-y-2\">\n              <Label htmlFor=\"password\">Password</Label>\n              <div className=\"relative\">\n                <Input\n                  className=\"pr-10\"\n                  id=\"password\"\n                  type={showPassword ? 'text' : 'password'}\n                />\n                <Button\n                  className=\"absolute top-0 right-0 h-full px-3 text-muted-foreground hover:bg-transparent\"\n                  onClick={() => setShowPassword(!showPassword)}\n                  size=\"icon\"\n                  type=\"button\"\n                  variant=\"ghost\"\n                >\n                  {showPassword ? (\n                    <EyeOff className=\"h-4 w-4\" />\n                  ) : (\n                    <Eye className=\"h-4 w-4\" />\n                  )}\n                </Button>\n              </div>\n            </div>\n\n            <div className=\"flex items-center space-x-2\">\n              <Checkbox id=\"terms\" />\n              <label className=\"text-muted-foreground text-sm\" htmlFor=\"terms\">\n                I agree to the{' '}\n                <Link className=\"text-primary hover:underline\" href=\"#\">\n                  Terms\n                </Link>{' '}\n                and{' '}\n                <Link className=\"text-primary hover:underline\" href=\"#\">\n                  Conditions\n                </Link>\n              </label>\n            </div>\n\n            <Button className=\"w-full bg-primary text-primary-foreground\">\n              Create free account\n            </Button>\n          </CardContent>\n          <CardFooter className=\"flex justify-center border-t py-4!\">\n            <p className=\"text-pretty text-center text-muted-foreground text-sm\">\n              Already have an account?{' '}\n              <Link className=\"text-primary hover:underline\" href=\"#\">\n                Sign in\n              </Link>\n            </p>\n          </CardFooter>\n        </Card>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "login"
      ]
    },
    {
      "name": "onboarding-01",
      "type": "registry:block",
      "title": "Onboarding",
      "description": "A onboarding block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "collapsible",
        "dropdown-menu"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/onboarding/onboarding-01.tsx",
          "type": "registry:component",
          "target": "components/onboarding-01.tsx",
          "content": "'use client';\n\nimport {\n  IconArchive,\n  IconChevronRight,\n  IconCircleCheckFilled,\n  IconCircleDashed,\n  IconDots,\n  IconMail,\n} from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Button, buttonVariants } from '@/components/ui/button';\nimport { Collapsible, CollapsibleContent } from '@/components/ui/collapsible';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { cn } from '@/lib/utils';\n\nconst steps = [\n  {\n    id: 'profile',\n    title: 'Complete your profile',\n    description:\n      'Add your name, photo, and role so your team knows who you are.',\n    completed: true,\n    actionLabel: 'Edit profile',\n    actionHref: '#',\n  },\n  {\n    id: 'workspace',\n    title: 'Set up your workspace',\n    description:\n      'Customize your workspace with a name, icon, and default settings.',\n    completed: false,\n    actionLabel: 'Configure workspace',\n    actionHref: '#',\n  },\n  {\n    id: 'invite',\n    title: 'Invite your team',\n    description:\n      'Bring your teammates on board so you can collaborate in real time.',\n    completed: false,\n    actionLabel: 'Send invites',\n    actionHref: '#',\n  },\n  {\n    id: 'integrations',\n    title: 'Connect integrations',\n    description:\n      'Link your favorite tools like Slack, GitHub, and Linear to streamline workflows.',\n    completed: false,\n    actionLabel: 'Browse integrations',\n    actionHref: '#',\n  },\n  {\n    id: 'workflow',\n    title: 'Create your first workflow',\n    description:\n      'Automate a repetitive task with a simple drag-and-drop workflow builder.',\n    completed: false,\n    actionLabel: 'Build workflow',\n    actionHref: '#',\n  },\n  {\n    id: 'notifications',\n    title: 'Set up notifications',\n    description:\n      'Choose how and when you want to be notified about updates and mentions.',\n    completed: false,\n    actionLabel: 'Manage notifications',\n    actionHref: '#',\n  },\n];\n\ninterface OnboardingStep {\n  id: string;\n  title: string;\n  description: string;\n  completed: boolean;\n  actionLabel: string;\n  actionHref: string;\n}\n\nfunction CircularProgress({\n  completed,\n  total,\n}: {\n  completed: number;\n  total: number;\n}) {\n  const progress = total > 0 ? ((total - completed) / total) * 100 : 0;\n  const strokeDashoffset = 100 - progress;\n\n  return (\n    <svg className=\"-rotate-90\" height=\"14\" viewBox=\"0 0 14 14\" width=\"14\">\n      <circle\n        className=\"stroke-muted\"\n        cx=\"7\"\n        cy=\"7\"\n        fill=\"none\"\n        pathLength=\"100\"\n        r=\"6\"\n        strokeWidth=\"2\"\n      />\n      <circle\n        className=\"stroke-primary\"\n        cx=\"7\"\n        cy=\"7\"\n        fill=\"none\"\n        pathLength=\"100\"\n        r=\"6\"\n        strokeDasharray=\"100\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"2\"\n        style={{ strokeDashoffset }}\n      />\n    </svg>\n  );\n}\n\nfunction StepIndicator({ completed }: { completed: boolean }) {\n  if (completed) {\n    return (\n      <IconCircleCheckFilled\n        aria-hidden=\"true\"\n        className=\"mt-1 size-4.5 shrink-0 text-primary\"\n      />\n    );\n  }\n  return (\n    <IconCircleDashed\n      aria-hidden=\"true\"\n      className=\"mt-1 size-5 shrink-0 stroke-muted-foreground/40\"\n      strokeWidth={2}\n    />\n  );\n}\n\nexport function Onboarding01() {\n  const [currentSteps, setCurrentSteps] = useState<OnboardingStep[]>(steps);\n  const [openStepId, setOpenStepId] = useState<string | null>(() => {\n    const firstIncomplete = steps.find((s) => !s.completed);\n    return firstIncomplete?.id ?? steps[0]?.id ?? null;\n  });\n  const [dismissed, setDismissed] = useState(false);\n\n  const completedCount = currentSteps.filter((s) => s.completed).length;\n  const remainingCount = currentSteps.length - completedCount;\n\n  const handleStepClick = (stepId: string) => {\n    setOpenStepId(openStepId === stepId ? null : stepId);\n  };\n\n  const handleStepAction = (step: OnboardingStep) => {\n    const updated = currentSteps.map((s) =>\n      s.id === step.id ? { ...s, completed: true } : s\n    );\n    setCurrentSteps(updated);\n    const nextIncomplete = updated.find((s) => !s.completed);\n    setOpenStepId(nextIncomplete?.id ?? null);\n  };\n\n  if (dismissed) {\n    return (\n      <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n        <div className=\"text-center\">\n          <p className=\"text-pretty text-muted-foreground\">\n            Checklist dismissed\n          </p>\n          <button\n            className=\"mt-2 text-primary text-sm underline\"\n            onClick={() => setDismissed(false)}\n          >\n            Show again\n          </button>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"w-full max-w-lg\">\n        <div className=\"w-md rounded-lg border bg-card p-4 text-card-foreground shadow-xs\">\n          <div className=\"mr-2 mb-4 flex flex-col justify-between sm:flex-row sm:items-center\">\n            <h3 className=\"ml-2 text-balance font-semibold text-foreground\">\n              Get started with Acme\n            </h3>\n            <div className=\"mt-2 flex items-center justify-end sm:mt-0\">\n              <CircularProgress\n                completed={remainingCount}\n                total={currentSteps.length}\n              />\n              <div className=\"mr-3 ml-1.5 text-muted-foreground text-sm\">\n                <span className=\"font-medium text-foreground\">\n                  {completedCount}\n                </span>\n                {' / '}\n                <span className=\"font-medium text-foreground\">\n                  {currentSteps.length}\n                </span>{' '}\n                completed\n              </div>\n              <DropdownMenu>\n                <DropdownMenuTrigger\n                  render={\n                    <Button className=\"h-6 w-6\" size=\"icon\" variant=\"ghost\" />\n                  }\n                >\n                  <IconDots aria-hidden=\"true\" className=\"h-4 w-4 shrink-0\" />\n                  <span className=\"sr-only\">Options</span>\n                </DropdownMenuTrigger>\n                <DropdownMenuContent align=\"end\" className=\"w-40\">\n                  <DropdownMenuItem onClick={() => setDismissed(true)}>\n                    <IconArchive\n                      aria-hidden=\"true\"\n                      className=\"mr-2 h-4 w-4 shrink-0\"\n                    />\n                    Dismiss\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    onClick={() =>\n                      window.open('mailto:support@acme.com?subject=Feedback')\n                    }\n                  >\n                    <IconMail\n                      aria-hidden=\"true\"\n                      className=\"mr-2 h-4 w-4 shrink-0\"\n                    />\n                    Give feedback\n                  </DropdownMenuItem>\n                </DropdownMenuContent>\n              </DropdownMenu>\n            </div>\n          </div>\n\n          <div className=\"space-y-0\">\n            {currentSteps.map((step, index) => {\n              const isOpen = openStepId === step.id;\n              const isFirst = index === 0;\n              const prevStep = currentSteps[index - 1];\n              const isPrevOpen = prevStep && openStepId === prevStep.id;\n\n              const showBorderTop = !(isFirst || isOpen || isPrevOpen);\n\n              return (\n                <div\n                  className={cn(\n                    'group',\n                    isOpen && 'rounded-lg',\n                    showBorderTop && 'border-border border-t'\n                  )}\n                  key={step.id}\n                >\n                  <div\n                    className={cn(\n                      'block w-full cursor-pointer text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n                      isOpen && 'rounded-lg'\n                    )}\n                    onClick={() => handleStepClick(step.id)}\n                    onKeyDown={(e) => {\n                      if (e.key === 'Enter' || e.key === ' ') {\n                        e.preventDefault();\n                        handleStepClick(step.id);\n                      }\n                    }}\n                    role=\"button\"\n                    tabIndex={0}\n                  >\n                    <div\n                      className={cn(\n                        'relative overflow-hidden rounded-lg transition-colors',\n                        isOpen && 'border border-border bg-muted'\n                      )}\n                    >\n                      <div className=\"relative flex items-center justify-between gap-3 py-3 pr-2 pl-4\">\n                        <div className=\"flex w-full gap-3\">\n                          <div className=\"shrink-0\">\n                            <StepIndicator completed={step.completed} />\n                          </div>\n                          <div className=\"mt-0.5 grow\">\n                            <h4\n                              className={cn(\n                                'font-semibold',\n                                step.completed\n                                  ? 'text-primary'\n                                  : 'text-foreground'\n                              )}\n                            >\n                              {step.title}\n                            </h4>\n                            <Collapsible open={isOpen}>\n                              <CollapsibleContent>\n                                <p className=\"mt-2 text-pretty text-muted-foreground text-sm sm:max-w-64 md:max-w-xs\">\n                                  {step.description}\n                                </p>\n                                <a\n                                  className={cn(\n                                    buttonVariants({ size: 'sm' }),\n                                    'mt-3'\n                                  )}\n                                  href={step.actionHref}\n                                  onClick={(e) => {\n                                    e.stopPropagation();\n                                    handleStepAction(step);\n                                  }}\n                                >\n                                  {step.actionLabel}\n                                </a>\n                              </CollapsibleContent>\n                            </Collapsible>\n                          </div>\n                        </div>\n                        {!isOpen && (\n                          <IconChevronRight\n                            aria-hidden=\"true\"\n                            className=\"h-4 w-4 shrink-0 text-muted-foreground\"\n                          />\n                        )}\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              );\n            })}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "onboarding"
      ]
    },
    {
      "name": "onboarding-02",
      "type": "registry:block",
      "title": "Onboarding Workspace Setup",
      "description": "A onboarding workspace setup block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/onboarding/onboarding-02.tsx",
          "type": "registry:component",
          "target": "components/onboarding-02.tsx",
          "content": "'use client';\n\nimport type { Icon } from '@tabler/icons-react';\nimport {\n  IconCircleCheckFilled,\n  IconFolder,\n  IconPlug,\n  IconRocket,\n  IconUsers,\n} from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\ninterface StepData {\n  title: string;\n  description: string;\n  icon: Icon;\n  actionLabel: string;\n}\n\nconst steps: StepData[] = [\n  {\n    title: 'Create your workspace',\n    description:\n      'Name your workspace and pick a URL. This is where your team will collaborate on everything.',\n    icon: IconRocket,\n    actionLabel: 'Get started',\n  },\n  {\n    title: 'Invite your team',\n    description:\n      'Add teammates by email so everyone can collaborate in real time across projects.',\n    icon: IconUsers,\n    actionLabel: 'Send invites',\n  },\n  {\n    title: 'Set up your first project',\n    description:\n      'Create a project to organize tasks, docs, and milestones all in one place.',\n    icon: IconFolder,\n    actionLabel: 'Create project',\n  },\n  {\n    title: 'Connect your tools',\n    description:\n      'Link Slack, GitHub, or Figma to keep your workflow in sync automatically.',\n    icon: IconPlug,\n    actionLabel: 'Browse integrations',\n  },\n];\n\nfunction getIconColor(isCompleted: boolean, isActive: boolean) {\n  if (isCompleted) {\n    return 'text-muted-foreground/30';\n  }\n  if (isActive) {\n    return 'text-primary';\n  }\n  return 'text-muted-foreground/40';\n}\n\nfunction getTitleClass(isCompleted: boolean, isActive: boolean) {\n  if (isCompleted) {\n    return 'text-muted-foreground/50 line-through';\n  }\n  if (isActive) {\n    return 'text-foreground';\n  }\n  return 'text-muted-foreground';\n}\n\nfunction StepIndicator({\n  index,\n  isCompleted,\n  isActive,\n}: {\n  index: number;\n  isCompleted: boolean;\n  isActive: boolean;\n}) {\n  if (isCompleted) {\n    return (\n      <div className=\"flex size-7 items-center justify-center\">\n        <IconCircleCheckFilled\n          aria-hidden=\"true\"\n          className=\"size-7 text-emerald-500\"\n        />\n      </div>\n    );\n  }\n  return (\n    <div\n      className={cn(\n        'flex size-7 items-center justify-center rounded-full font-semibold text-xs',\n        isActive\n          ? 'bg-primary text-primary-foreground'\n          : 'bg-muted text-muted-foreground'\n      )}\n    >\n      {index + 1}\n    </div>\n  );\n}\n\nfunction StepCard({\n  step,\n  index,\n  isCompleted,\n  isActive,\n  onComplete,\n}: {\n  step: StepData;\n  index: number;\n  isCompleted: boolean;\n  isActive: boolean;\n  onComplete: () => void;\n}) {\n  const StepIcon = step.icon;\n\n  return (\n    <div\n      className={cn(\n        'rounded-lg border p-4 transition-colors',\n        isActive && 'border-primary/30 bg-muted/50',\n        !isActive && 'border-border bg-background'\n      )}\n    >\n      <div className=\"flex gap-3\">\n        <div className=\"mt-0.5 shrink-0\">\n          <StepIndicator\n            index={index}\n            isActive={isActive}\n            isCompleted={isCompleted}\n          />\n        </div>\n        <div className=\"min-w-0 flex-1\">\n          <div className=\"flex items-start gap-3\">\n            <div className=\"min-w-0 flex-1\">\n              <p\n                className={cn(\n                  'font-medium leading-6',\n                  getTitleClass(isCompleted, isActive)\n                )}\n              >\n                {step.title}\n              </p>\n              <p\n                className={cn(\n                  'mt-0.5 text-sm leading-5',\n                  isActive\n                    ? 'text-muted-foreground'\n                    : 'text-muted-foreground/60'\n                )}\n              >\n                {step.description}\n              </p>\n              {isActive && (\n                <Button className=\"mt-3\" onClick={onComplete} size=\"sm\">\n                  <StepIcon\n                    aria-hidden=\"true\"\n                    className=\"-ml-0.5 size-4 shrink-0\"\n                  />\n                  {step.actionLabel}\n                </Button>\n              )}\n            </div>\n            <StepIcon\n              aria-hidden=\"true\"\n              className={cn(\n                'mt-0.5 size-5 shrink-0',\n                getIconColor(isCompleted, isActive)\n              )}\n            />\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport function Onboarding02() {\n  const [completedSteps, setCompletedSteps] = useState<Set<number>>(\n    () => new Set([0])\n  );\n\n  const currentStep = steps.findIndex((_, i) => !completedSteps.has(i));\n  const completedCount = completedSteps.size;\n  const allDone = completedCount === steps.length;\n\n  const handleComplete = (index: number) => {\n    setCompletedSteps((prev) => {\n      const next = new Set(prev);\n      next.add(index);\n      return next;\n    });\n  };\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"w-full max-w-xl\">\n        <div className=\"mb-6\">\n          <h3 className=\"font-semibold text-foreground text-lg\">\n            Set up your workspace\n          </h3>\n          <p className=\"mt-1 text-muted-foreground text-sm\">\n            Complete these steps to get your team up and running\n          </p>\n          <div className=\"mt-4\">\n            <div className=\"flex items-center justify-between text-sm\">\n              <span className=\"text-muted-foreground\">\n                {allDone ? (\n                  <span className=\"font-medium text-emerald-600\">\n                    All done 🎉\n                  </span>\n                ) : (\n                  <>\n                    <span className=\"font-medium text-foreground\">\n                      {completedCount}\n                    </span>{' '}\n                    of {steps.length} completed\n                  </>\n                )}\n              </span>\n            </div>\n            <div className=\"mt-2 h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n              <div\n                className=\"h-full rounded-full bg-emerald-500 transition-all duration-500\"\n                style={{\n                  width: `${(completedCount / steps.length) * 100}%`,\n                }}\n              />\n            </div>\n          </div>\n        </div>\n\n        <div className=\"space-y-3\">\n          {steps.map((step, index) => (\n            <StepCard\n              index={index}\n              isActive={index === currentStep}\n              isCompleted={completedSteps.has(index)}\n              key={step.title}\n              onComplete={() => handleComplete(index)}\n              step={step}\n            />\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "onboarding"
      ]
    },
    {
      "name": "onboarding-03",
      "type": "registry:block",
      "title": "Onboarding Steps with Progress",
      "description": "A onboarding steps with progress block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "progress"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/onboarding/onboarding-03.tsx",
          "type": "registry:component",
          "target": "components/onboarding-03.tsx",
          "content": "'use client';\n\nimport { IconCircleCheckFilled } from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Progress } from '@/components/ui/progress';\nimport { cn } from '@/lib/utils';\n\nconst steps = [\n  {\n    id: '1.',\n    title: 'Create your store',\n    description:\n      \"Choose a name, pick a subdomain, and configure your store's basic settings.\",\n  },\n  {\n    id: '2.',\n    title: 'Add your products',\n    description:\n      'Upload your catalog with photos, pricing, and inventory details.',\n  },\n  {\n    id: '3.',\n    title: 'Set up payments',\n    description:\n      'Connect Stripe or PayPal to start accepting orders from customers.',\n  },\n  {\n    id: '4.',\n    title: 'Launch your store',\n    description: 'Review everything and publish your storefront to the world.',\n  },\n];\n\nexport function Onboarding03() {\n  const [activeStep, setActiveStep] = useState(0);\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"sm:mx-auto sm:max-w-lg\">\n        <h3 className=\"font-semibold text-foreground text-lg\">Store setup</h3>\n        <p className=\"mt-1 text-muted-foreground text-sm leading-6\">\n          Complete each step to launch your online store.\n        </p>\n        <div className=\"mt-4 flex items-center justify-end space-x-4\">\n          <span className=\"text-muted-foreground text-sm\">\n            Step {activeStep + 1}/{steps.length}\n          </span>\n          <Progress\n            className=\"w-32\"\n            value={(activeStep / steps.length) * 100}\n          />\n        </div>\n        <ul className=\"mt-4 space-y-4\">\n          {steps.map((step, index) => (\n            <li key={step.id}>\n              <button\n                className={cn(\n                  'relative w-full cursor-pointer rounded-lg border bg-card p-4 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n                  index === activeStep\n                    ? 'border-foreground/20'\n                    : 'border-border'\n                )}\n                onClick={() => setActiveStep(index)}\n                type=\"button\"\n              >\n                <div className=\"flex items-start space-x-3\">\n                  {index < activeStep ? (\n                    <IconCircleCheckFilled\n                      aria-hidden={true}\n                      className=\"size-6 shrink-0 text-foreground\"\n                    />\n                  ) : (\n                    <span\n                      aria-hidden={true}\n                      className=\"flex size-6 items-center justify-center font-medium text-muted-foreground\"\n                    >\n                      {step.id}\n                    </span>\n                  )}\n                  <div>\n                    <h3\n                      className={cn(\n                        'font-medium',\n                        index < activeStep\n                          ? 'text-muted-foreground line-through'\n                          : 'text-foreground'\n                      )}\n                    >\n                      {step.title}\n                    </h3>\n                    <p className=\"mt-1 text-muted-foreground text-sm leading-6\">\n                      {step.description}\n                    </p>\n                  </div>\n                </div>\n              </button>\n            </li>\n          ))}\n        </ul>\n        <div className=\"mt-6 rounded-lg bg-muted p-4\">\n          <h4 className=\"font-medium text-foreground text-sm\">Need help?</h4>\n          <p className=\"mt-1 text-muted-foreground text-sm\">\n            Connect with a member of our team at{' '}\n            <a\n              className=\"font-medium text-primary\"\n              href=\"mailto:help@example.com\"\n            >\n              help@example.com\n            </a>\n            .\n          </p>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "onboarding"
      ]
    },
    {
      "name": "onboarding-04",
      "type": "registry:block",
      "title": "Onboarding Accordion",
      "description": "A onboarding accordion block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "accordion",
        "button"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/onboarding/onboarding-04.tsx",
          "type": "registry:component",
          "target": "components/onboarding-04.tsx",
          "content": "'use client';\n\nimport {\n  IconAdjustments,\n  IconCalculator,\n  IconChartBar,\n  IconCircleCheckFilled,\n  IconDatabase,\n} from '@tabler/icons-react';\nimport { useState } from 'react';\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from '@/components/ui/accordion';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\nconst steps = [\n  {\n    title: 'Configure your environment',\n    subtitle: 'Environment ready',\n    icon: IconAdjustments,\n    description:\n      'Set up your development environment with API keys and preferences.',\n    buttonText: 'Edit settings',\n  },\n  {\n    title: 'Connect your database',\n    subtitle: 'Add connection',\n    icon: IconDatabase,\n    description:\n      'Link your PostgreSQL, MySQL, or MongoDB instance to get started.',\n    buttonText: 'Add database',\n  },\n  {\n    title: 'Define your schema',\n    subtitle: 'Create schema',\n    icon: IconCalculator,\n    description: 'Model your data with types, relations, and validation rules.',\n    buttonText: 'Open editor',\n  },\n  {\n    title: 'Build your first API',\n    subtitle: 'Generate endpoints',\n    icon: IconChartBar,\n    description:\n      'Auto-generate REST or GraphQL endpoints from your schema in seconds.',\n    buttonText: 'Generate API',\n  },\n];\n\nexport function Onboarding04() {\n  const [activeStep, setActiveStep] = useState(1);\n\n  const [openItem, setOpenItem] = useState(\n    activeStep < steps.length ? steps[activeStep].title : ''\n  );\n\n  const handleComplete = () => {\n    const next = activeStep + 1;\n    setActiveStep(next);\n    setOpenItem(next < steps.length ? steps[next].title : '');\n  };\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"mx-auto w-full min-w-0 max-w-sm sm:min-w-sm\">\n        <h3 className=\"font-semibold text-foreground text-lg\">Quick start</h3>\n        <p className=\"mt-1 text-muted-foreground text-sm\">\n          Set up your backend in four simple steps\n        </p>\n        <Accordion\n          className=\"mt-6 space-y-2\"\n          onValueChange={(value) => setOpenItem(value[0] ?? '')}\n          value={openItem ? [openItem] : []}\n        >\n          {steps.map((step, index) => {\n            let status: 'complete' | 'current' | 'upcoming' = 'upcoming';\n            if (index < activeStep) {\n              status = 'complete';\n            } else if (index === activeStep) {\n              status = 'current';\n            }\n\n            return (\n              <AccordionItem\n                className=\"!border-b rounded-lg border shadow-xs\"\n                key={step.title}\n                value={step.title}\n              >\n                <AccordionTrigger className=\"px-4 hover:no-underline\">\n                  <div className=\"flex items-center space-x-2\">\n                    {status === 'complete' ? (\n                      <span\n                        aria-hidden\n                        className=\"flex size-5 items-center justify-center\"\n                      >\n                        <IconCircleCheckFilled\n                          aria-hidden\n                          className=\"size-6 shrink-0 text-foreground\"\n                        />\n                      </span>\n                    ) : (\n                      <span\n                        aria-hidden\n                        className=\"size-5 shrink-0 rounded-full border border-border\"\n                      />\n                    )}\n                    <p\n                      className={cn(\n                        'font-medium text-sm',\n                        status === 'upcoming'\n                          ? 'text-muted-foreground/60'\n                          : 'text-foreground'\n                      )}\n                    >\n                      {step.title}\n                    </p>\n                  </div>\n                </AccordionTrigger>\n                <AccordionContent className=\"px-4\">\n                  <div className=\"flex items-center justify-center rounded-md bg-muted px-4 py-5 ring-1 ring-ring ring-inset\">\n                    <div className=\"max-w-xs text-center\">\n                      <step.icon\n                        aria-hidden\n                        className=\"mx-auto size-7 shrink-0 text-muted-foreground\"\n                      />\n                      <p className=\"mt-4 font-semibold text-muted-foreground text-sm\">\n                        {step.subtitle}\n                      </p>\n                      <p className=\"mt-1 text-muted-foreground text-sm\">\n                        {step.description}\n                      </p>\n                      {status === 'complete' ? (\n                        <Button className=\"mt-6\" size=\"sm\" variant=\"outline\">\n                          {step.buttonText}\n                        </Button>\n                      ) : (\n                        <Button\n                          className=\"mt-6\"\n                          disabled={status === 'upcoming'}\n                          onClick={() => {\n                            if (status === 'current') {\n                              handleComplete();\n                            }\n                          }}\n                          size=\"sm\"\n                        >\n                          {step.buttonText}\n                        </Button>\n                      )}\n                    </div>\n                  </div>\n                </AccordionContent>\n              </AccordionItem>\n            );\n          })}\n        </Accordion>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "onboarding"
      ]
    },
    {
      "name": "onboarding-05",
      "type": "registry:block",
      "title": "Onboarding Activity Feed",
      "description": "A onboarding activity feed block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/onboarding/onboarding-05.tsx",
          "type": "registry:component",
          "target": "components/onboarding-05.tsx",
          "content": "import { cn } from '@/lib/utils';\n\nconst steps = [\n  {\n    id: 1,\n    type: 'created',\n    description: 'Created project \"Brand Refresh\"',\n    user: { name: 'Sarah K.', initial: 'S', bgColor: 'bg-violet-500' },\n    activityTime: '3d ago',\n  },\n  {\n    id: 2,\n    type: 'created',\n    description: 'Uploaded 12 design assets',\n    user: { name: 'Marcus L.', initial: 'M', bgColor: 'bg-orange-500' },\n    activityTime: '2d ago',\n  },\n  {\n    id: 3,\n    type: 'created',\n    description: 'Shared prototype with stakeholders',\n    user: { name: 'Sarah K.', initial: 'S', bgColor: 'bg-emerald-500' },\n    activityTime: '2h ago',\n  },\n  {\n    id: 4,\n    type: 'created',\n    description: 'Left feedback on homepage layout',\n    user: { name: 'Jamie R.', initial: 'J', bgColor: 'bg-fuchsia-500' },\n    activityTime: '5min ago',\n  },\n  {\n    id: 5,\n    type: 'in progress',\n    description: 'Scheduled design review meeting',\n    user: { name: 'Team Ops', initial: 'T', bgColor: 'bg-blue-500' },\n    activityTime: 'today 2:30pm',\n  },\n];\n\nexport function Onboarding05() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"sm:max-w-lg md:mx-auto\">\n        <h3 className=\"font-medium text-foreground\">Project activity</h3>\n        <p className=\"mt-1 text-muted-foreground text-sm leading-6\">\n          Recent updates from your team\n        </p>\n        <ul className=\"mt-6 space-y-6 pb-2\">\n          {steps.map((step, stepIdx) => (\n            <li className=\"relative flex gap-x-3\" key={step.id}>\n              <div\n                className={cn(\n                  'absolute top-0 left-0 flex w-6 justify-center',\n                  stepIdx === steps.length - 1 ? 'h-6' : '-bottom-6'\n                )}\n              >\n                <span aria-hidden className=\"w-px bg-border\" />\n              </div>\n              <div className=\"flex items-start space-x-2\">\n                <div className=\"flex items-center space-x-2\">\n                  <div className=\"relative flex size-6 flex-none items-center justify-center bg-background\">\n                    {step.type === 'created' ? (\n                      <div className=\"size-3 rounded-full border border-gray-300 bg-muted/50 ring-4 ring-background\" />\n                    ) : (\n                      <div className=\"size-3 rounded-full border border-gray-300 bg-background ring-4 ring-background\" />\n                    )}\n                  </div>\n                  <span\n                    aria-hidden\n                    className={cn(\n                      step.user.bgColor,\n                      'inline-flex size-6 flex-none items-center justify-center rounded-full text-primary-foreground text-xs'\n                    )}\n                  >\n                    {step.user.initial}\n                  </span>\n                </div>\n                <p className=\"mt-0.5 font-medium text-foreground text-sm\">\n                  {step.user.name}\n                  <span className=\"font-normal text-muted-foreground\">\n                    {' '}\n                    {step.description}\n                  </span>\n                  <span className=\"font-normal text-muted-foreground/60\">\n                    {' '}\n                    &#8729; {step.activityTime}\n                  </span>\n                </p>\n              </div>\n            </li>\n          ))}\n        </ul>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "onboarding"
      ]
    },
    {
      "name": "onboarding-06",
      "type": "registry:block",
      "title": "Onboarding Timeline",
      "description": "A onboarding timeline block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/onboarding/onboarding-06.tsx",
          "type": "registry:component",
          "target": "components/onboarding-06.tsx",
          "content": "import { IconCheck } from '@tabler/icons-react';\nimport { cn } from '@/lib/utils';\n\nconst steps = [\n  {\n    id: 1,\n    type: 'done',\n    title: 'Repository connected',\n    description: 'Linked GitHub repository to deployment pipeline',\n    activityTime: '3d ago',\n  },\n  {\n    id: 2,\n    type: 'done',\n    title: 'Build configuration set',\n    description: 'Configured build commands and environment variables',\n    activityTime: '2d ago',\n  },\n  {\n    id: 3,\n    type: 'done',\n    title: 'Domain configured',\n    description: 'Custom domain verified and SSL certificate issued',\n    activityTime: '31min ago',\n  },\n  {\n    id: 4,\n    type: 'in progress',\n    title: 'Running health checks',\n    description: 'Verifying all endpoints respond correctly',\n    activityTime: 'Running now...',\n  },\n  {\n    id: 5,\n    type: 'open',\n    title: 'Go live',\n    description: 'Switch traffic to the new deployment',\n    activityTime: 'Upcoming',\n  },\n];\n\nexport function Onboarding06() {\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"sm:mx-auto sm:max-w-lg\">\n        <h3 className=\"font-medium text-foreground\">Deployment progress</h3>\n        <ul className=\"mt-6 space-y-6\">\n          {steps.map((step, stepIdx) => (\n            <li className=\"relative flex gap-x-3\" key={step.id}>\n              <div\n                className={cn(\n                  'absolute top-0 left-0 flex w-6 justify-center',\n                  stepIdx === steps.length - 1 ? 'h-6' : '-bottom-6'\n                )}\n              >\n                <span aria-hidden className=\"w-px bg-border\" />\n              </div>\n              <div className=\"flex items-start space-x-2.5\">\n                <div className=\"relative flex size-6 flex-none items-center justify-center bg-background\">\n                  {step.type === 'done' ? (\n                    <IconCheck aria-hidden className=\"size-5 text-primary\" />\n                  ) : step.type === 'in progress' ? (\n                    <div\n                      aria-hidden\n                      className=\"size-2.5 rounded-full bg-primary ring-4 ring-background\"\n                    />\n                  ) : (\n                    <div\n                      aria-hidden\n                      className=\"size-3 rounded-full border border-border bg-background ring-4 ring-background\"\n                    />\n                  )}\n                </div>\n                <div>\n                  <p className=\"mt-0.5 font-medium text-foreground text-sm\">\n                    {step.title}{' '}\n                    <span className=\"font-normal text-muted-foreground/60\">\n                      &#8729; {step.activityTime}\n                    </span>\n                  </p>\n                  <p className=\"mt-0.5 text-muted-foreground text-sm leading-6\">\n                    {step.description}\n                  </p>\n                </div>\n              </div>\n            </li>\n          ))}\n        </ul>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "onboarding"
      ]
    },
    {
      "name": "onboarding-07",
      "type": "registry:block",
      "title": "Onboarding Deploy Pipeline",
      "description": "A onboarding deploy pipeline block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "accordion",
        "button",
        "progress"
      ],
      "dependencies": [
        "@tabler/icons-react"
      ],
      "files": [
        {
          "path": "content/components/onboarding/onboarding-07.tsx",
          "type": "registry:component",
          "target": "components/onboarding-07.tsx",
          "content": "'use client';\n\nimport {\n  IconCircleCheckFilled,\n  IconLoader2,\n  IconRefresh,\n} from '@tabler/icons-react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from '@/components/ui/accordion';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\nimport { cn } from '@/lib/utils';\n\nconst initialSteps = [\n  {\n    id: 1,\n    type: 'created' as const,\n    description: 'Export records',\n    value: 100,\n    createdOn: '2024-03-10 09:32',\n    runTime: '12min 18s',\n  },\n  {\n    id: 2,\n    type: 'created' as const,\n    description: 'Transform schema',\n    value: 100,\n    createdOn: '2024-03-10 10:03',\n    runTime: '18min 45s',\n  },\n  {\n    id: 3,\n    type: 'in progress' as const,\n    description: 'Import to new system',\n    value: 45,\n    createdOn: null as string | null,\n    runTime: null as string | null,\n  },\n];\n\nexport function Onboarding07() {\n  const [steps, setSteps] = useState(initialSteps);\n  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n  const allComplete = steps.every((s) => s.value === 100);\n\n  const startAnimation = useCallback(() => {\n    if (intervalRef.current) {\n      clearInterval(intervalRef.current);\n    }\n    intervalRef.current = setInterval(() => {\n      setSteps((prev) => {\n        const next = prev.map((step) => {\n          if (step.id !== 3 || step.value >= 100) {\n            return step;\n          }\n          const increment = Math.random() * 0.8 + 0.3;\n          const newValue = Math.min(step.value + increment, 100);\n          if (newValue >= 100) {\n            return {\n              ...step,\n              value: 100,\n              type: 'created' as const,\n              createdOn: '2024-03-10 10:47',\n              runTime: '6min 32s',\n            };\n          }\n          return { ...step, value: newValue };\n        });\n        if (next[2].value >= 100 && intervalRef.current) {\n          clearInterval(intervalRef.current);\n          intervalRef.current = null;\n        }\n        return next;\n      });\n    }, 50);\n  }, []);\n\n  useEffect(() => {\n    startAnimation();\n    return () => {\n      if (intervalRef.current) {\n        clearInterval(intervalRef.current);\n      }\n    };\n  }, [startAnimation]);\n\n  const handleRunAgain = () => {\n    setSteps(initialSteps);\n    startAnimation();\n  };\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"w-full max-w-xl\">\n        <div className=\"flex items-center justify-between\">\n          <h3 className=\"font-semibold text-foreground\">Data Migration</h3>\n          <Button\n            className={\n              allComplete ? 'opacity-100' : 'pointer-events-none opacity-0'\n            }\n            onClick={handleRunAgain}\n            size=\"icon\"\n            variant=\"secondary\"\n          >\n            <IconRefresh aria-hidden=\"true\" className=\"size-4\" />\n          </Button>\n        </div>\n        <p className=\"mt-1 text-muted-foreground text-sm leading-6\">\n          Migrating records from your legacy system\n        </p>\n        <div className=\"mt-6 flex items-center space-x-2\">\n          {steps.map((step) => (\n            <div className=\"w-full truncate\" key={step.id}>\n              <Progress className=\"h-1.5\" value={step.value} />\n              <div className=\"mt-2 flex items-center space-x-1 truncate\">\n                {step.value === 100 ? (\n                  <IconCircleCheckFilled\n                    aria-hidden={true}\n                    className=\"size-4 shrink-0 text-primary\"\n                  />\n                ) : (\n                  <IconLoader2\n                    aria-hidden={true}\n                    className=\"size-4 shrink-0 animate-spin text-primary\"\n                  />\n                )}\n                <p className=\"truncate text-muted-foreground text-xs\">\n                  {step.description}\n                </p>\n              </div>\n            </div>\n          ))}\n        </div>\n        <Accordion className=\"mt-8\" defaultValue={['logs']}>\n          <AccordionItem className=\"rounded-sm border-b-0\" value=\"logs\">\n            <AccordionTrigger className=\"font-medium text-foreground text-sm\">\n              Logs overview ({steps.length})\n            </AccordionTrigger>\n            <AccordionContent>\n              <ul className=\"mt-2 space-y-6 pb-2\">\n                {steps.map((step, stepIdx) => (\n                  <li className=\"relative flex gap-x-3\" key={step.id}>\n                    <div\n                      className={cn(\n                        'absolute top-0 left-0 flex w-6 justify-center',\n                        stepIdx === steps.length - 1 ? 'h-6' : '-bottom-6'\n                      )}\n                    >\n                      <span aria-hidden={true} className=\"w-px bg-border\" />\n                    </div>\n                    <div className=\"flex items-start space-x-2.5\">\n                      <div className=\"relative flex size-6 flex-none items-center justify-center bg-background\">\n                        {step.type === 'created' ? (\n                          <div className=\"size-3 rounded-full border border-border bg-muted/50 ring-4 ring-background\" />\n                        ) : (\n                          <div className=\"size-3 rounded-full border border-border bg-background ring-4 ring-background\" />\n                        )}\n                      </div>\n                      <div>\n                        <p className=\"mt-0.5 font-medium text-foreground text-sm\">\n                          {step.description}\n                        </p>\n                        {step.type === 'created' ? (\n                          <p className=\"text-muted-foreground text-sm leading-6\">\n                            Created on {step.createdOn}, Runtime: {step.runTime}\n                          </p>\n                        ) : (\n                          <p className=\"text-muted-foreground text-sm leading-6\">\n                            12,847 of 28,500 records processed...\n                          </p>\n                        )}\n                      </div>\n                    </div>\n                  </li>\n                ))}\n              </ul>\n            </AccordionContent>\n          </AccordionItem>\n        </Accordion>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "onboarding"
      ]
    },
    {
      "name": "sidebar-01",
      "type": "registry:block",
      "title": "Sidebar Collapsible Sections",
      "description": "A sidebar collapsible sections block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "button",
        "collapsible",
        "command",
        "dropdown-menu",
        "sidebar",
        "tooltip"
      ],
      "dependencies": [
        "@tabler/icons-react",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/sidebar/sidebar-01/nav-footer.tsx",
          "type": "registry:component",
          "target": "components/sidebar-01/nav-footer.tsx",
          "content": "'use client';\n\nimport {\n  BookmarkPlus,\n  CircleHelp,\n  LogOut,\n  Plus,\n  PlusCircle,\n  Puzzle,\n  Settings,\n  User,\n} from 'lucide-react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  SidebarFooter,\n  SidebarMenu,\n  SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from '@/components/ui/tooltip';\n\nexport function NavFooter({\n  user,\n}: {\n  user: {\n    name: string;\n    email: string;\n    avatar: string;\n  };\n}) {\n  return (\n    <SidebarFooter className=\"p-4\">\n      <SidebarMenu>\n        <SidebarMenuItem>\n          <div className=\"flex items-center justify-between gap-2\">\n            <div className=\"flex items-center gap-2\">\n              <DropdownMenu>\n                <DropdownMenuTrigger\n                  nativeButton={false}\n                  render={<Avatar className=\"h-8 w-8 rounded-full\" />}\n                >\n                  <AvatarImage alt={user.name} src={user.avatar} />\n                  <AvatarFallback className=\"rounded-full\">CN</AvatarFallback>\n                </DropdownMenuTrigger>\n                <DropdownMenuContent className=\"m-2\">\n                  <DropdownMenuItem>\n                    <User aria-hidden=\"true\" className=\"opacity-80\" size={16} />\n                    Profile\n                  </DropdownMenuItem>\n                  <DropdownMenuItem>\n                    <Settings\n                      aria-hidden=\"true\"\n                      className=\"opacity-80\"\n                      size={16}\n                    />\n                    Settings\n                  </DropdownMenuItem>\n                  <DropdownMenuItem>\n                    <LogOut\n                      aria-hidden=\"true\"\n                      className=\"opacity-80\"\n                      size={16}\n                    />\n                    Logout\n                  </DropdownMenuItem>\n                </DropdownMenuContent>\n              </DropdownMenu>\n\n              <TooltipProvider delay={0}>\n                <Tooltip>\n                  <TooltipTrigger\n                    render={\n                      <CircleHelp\n                        aria-hidden=\"true\"\n                        className=\"cursor-pointer opacity-60 hover:opacity-100\"\n                        size={16}\n                      />\n                    }\n                  />\n                  <TooltipContent\n                    className=\"m-2 max-w-[150px] border bg-popover px-2 py-1 text-popover-foreground\"\n                    side=\"top\"\n                  >\n                    <div className=\"space-y-1 text-xs\">\n                      <p className=\"font-medium\">User Information</p>\n                      <p className=\"text-muted-foreground\">\n                        More details about the current user or section can be\n                        displayed here.\n                      </p>\n                    </div>\n                  </TooltipContent>\n                </Tooltip>\n              </TooltipProvider>\n            </div>\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <Button\n                    aria-label=\"Open edit menu\"\n                    className=\"rounded-full shadow-none focus-visible:ring-0 focus-visible:ring-offset-0\"\n                    size=\"icon\"\n                    variant=\"ghost\"\n                  />\n                }\n              >\n                <Plus aria-hidden=\"true\" size={16} />\n              </DropdownMenuTrigger>\n              <DropdownMenuContent className=\"pb-2\">\n                <DropdownMenuLabel>Add New</DropdownMenuLabel>\n                <DropdownMenuItem>\n                  <PlusCircle\n                    aria-hidden=\"true\"\n                    className=\"mr-2 opacity-80\"\n                    size={16}\n                  />\n                  Add New Item\n                </DropdownMenuItem>\n                <DropdownMenuItem>\n                  <BookmarkPlus\n                    aria-hidden=\"true\"\n                    className=\"mr-2 opacity-80\"\n                    size={16}\n                  />\n                  Add Bookmark\n                </DropdownMenuItem>\n                <DropdownMenuItem>\n                  <Puzzle\n                    aria-hidden=\"true\"\n                    className=\"mr-2 opacity-80\"\n                    size={16}\n                  />\n                  Add Integration\n                </DropdownMenuItem>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </div>\n        </SidebarMenuItem>\n      </SidebarMenu>\n    </SidebarFooter>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-01/nav-header.tsx",
          "type": "registry:component",
          "target": "components/sidebar-01/nav-header.tsx",
          "content": "'use client';\n\nimport { Search } from 'lucide-react';\nimport * as React from 'react';\nimport { useEffect } from 'react';\nimport {\n  Command,\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n} from '@/components/ui/command';\nimport { SidebarHeader } from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport type { SidebarData } from '@/components/sidebar-01/types';\n\ninterface NavHeaderProps {\n  data: SidebarData;\n}\n\nexport function NavHeader({ data }: NavHeaderProps) {\n  const [open, setOpen] = React.useState(false);\n\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault();\n        setOpen((open) => !open);\n      }\n    };\n    document.addEventListener('keydown', down);\n    return () => document.removeEventListener('keydown', down);\n  }, []);\n\n  return (\n    <>\n      <SidebarHeader>\n        <div\n          className=\"flex cursor-pointer items-center justify-between px-2 pt-3 pb-0\"\n          onClick={() => setOpen(true)}\n        >\n          <div className=\"flex flex-1 items-center gap-3\">\n            <Search className=\"h-4 w-4 text-muted-foreground\" />\n            <span className=\"font-normal text-muted-foreground text-sm\">\n              Search\n            </span>\n          </div>\n          <div className=\"flex items-center justify-center rounded-md border border-border px-2 py-1\">\n            <kbd className=\"inline-flex font-[inherit] font-medium text-muted-foreground text-xs\">\n              <span className=\"opacity-70\">⌘K</span>\n            </kbd>\n          </div>\n        </div>\n      </SidebarHeader>\n\n      <CommandDialog onOpenChange={setOpen} open={open}>\n        <Command>\n          <CommandInput placeholder=\"Search everything...\" />\n          <CommandList>\n            <CommandEmpty>No results found.</CommandEmpty>\n            <CommandGroup heading=\"Navigation\">\n              {data.navMain.map((item) => (\n                <CommandItem\n                  className=\"py-2!\"\n                  key={item.id}\n                  onSelect={() => {\n                    setOpen(false);\n                  }}\n                >\n                  <item.icon className=\"mr-2 h-4 w-4\" />\n                  <span>{item.title}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n            <CommandSeparator className=\"my-2\" />\n            <CommandGroup heading=\"Favorites\">\n              {data.navCollapsible.favorites.map((item) => (\n                <CommandItem\n                  className=\"py-2!\"\n                  key={item.id}\n                  onSelect={() => {\n                    setOpen(false);\n                  }}\n                >\n                  <div\n                    className={cn('mr-2 h-3 w-3 rounded-full', item.color)}\n                  />\n                  <span>{item.title}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n            <CommandSeparator className=\"my-2\" />\n            <CommandGroup heading=\"Teams\">\n              {data.navCollapsible.teams.map((item) => (\n                <CommandItem\n                  className=\"py-2!\"\n                  key={item.id}\n                  onSelect={() => {\n                    setOpen(false);\n                  }}\n                >\n                  <item.icon className=\"mr-2 h-4 w-4\" />\n                  <span>{item.title}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n            <CommandSeparator className=\"my-2\" />\n            <CommandGroup heading=\"Topics\">\n              {data.navCollapsible.topics.map((item) => (\n                <CommandItem\n                  className=\"py-2!\"\n                  key={item.id}\n                  onSelect={() => {\n                    setOpen(false);\n                  }}\n                >\n                  <item.icon className=\"mr-2 h-4 w-4\" />\n                  <span>{item.title}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n          </CommandList>\n        </Command>\n      </CommandDialog>\n    </>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-01/types.ts",
          "type": "registry:file",
          "target": "components/sidebar-01/types.ts",
          "content": "import type { ElementType } from 'react';\n\nexport interface NavItem {\n  id: string;\n  title: string;\n  icon: ElementType;\n  url?: string;\n  isActive?: boolean;\n}\n\nexport interface User {\n  name: string;\n  email: string;\n  avatar: string;\n}\n\nexport interface FavoriteItem {\n  id: string;\n  title: string;\n  href: string;\n  color: string;\n}\n\nexport interface TeamItem {\n  id: string;\n  title: string;\n  icon: ElementType;\n}\n\nexport interface TopicItem {\n  id: string;\n  title: string;\n  icon: ElementType;\n}\n\nexport interface SidebarData {\n  user: User;\n  navMain: NavItem[];\n  navCollapsible: {\n    favorites: FavoriteItem[];\n    teams: TeamItem[];\n    topics: TopicItem[];\n  };\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-01/nav-main.tsx",
          "type": "registry:component",
          "target": "components/sidebar-01/nav-main.tsx",
          "content": "'use client';\n\nimport {\n  SidebarGroup,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport type { NavItem } from '@/components/sidebar-01/types';\n\nexport function NavMain({ items }: { items: NavItem[] }) {\n  return (\n    <SidebarGroup>\n      <SidebarMenu>\n        {items.map((item) => {\n          const Icon = item.icon;\n\n          return (\n            <SidebarMenuItem key={item.id}>\n              <SidebarMenuButton tooltip={item.title}>\n                {Icon && <Icon className=\"mr-2 h-4 w-4\" />}\n                <span>{item.title}</span>\n              </SidebarMenuButton>\n            </SidebarMenuItem>\n          );\n        })}\n      </SidebarMenu>\n    </SidebarGroup>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-01/index.tsx",
          "type": "registry:component",
          "target": "components/sidebar-01/index.tsx",
          "content": "import {\n  SidebarInset,\n  SidebarProvider,\n  SidebarTrigger,\n} from '@/components/ui/sidebar';\nimport { AppSidebar } from '@/components/sidebar-01/app-sidebar';\n\nexport default function Sidebar01() {\n  return (\n    <SidebarProvider>\n      <AppSidebar />\n      <SidebarInset>\n        <header className=\"flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12\">\n          <div className=\"flex items-center gap-2 px-4\">\n            <SidebarTrigger className=\"sm:hidden\" />\n          </div>\n        </header>\n      </SidebarInset>\n    </SidebarProvider>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-01/app-sidebar.tsx",
          "type": "registry:component",
          "target": "components/sidebar-01/app-sidebar.tsx",
          "content": "'use client';\n\nimport {\n  IconAd2,\n  IconBellRinging,\n  IconCalendar,\n  IconCalendarStats,\n  IconListDetails,\n  IconNews,\n  IconNotebook,\n  IconProgressCheck,\n  IconSettingsCode,\n} from '@tabler/icons-react';\nimport { LayoutDashboard, Package } from 'lucide-react';\nimport { Sidebar, SidebarContent } from '@/components/ui/sidebar';\nimport { NavCollapsible } from '@/components/sidebar-01/nav-collapsible';\nimport { NavFooter } from '@/components/sidebar-01/nav-footer';\nimport { NavHeader } from '@/components/sidebar-01/nav-header';\nimport { NavMain } from '@/components/sidebar-01/nav-main';\nimport type { SidebarData } from '@/components/sidebar-01/types';\n\nconst data: SidebarData = {\n  user: {\n    name: 'ephraim',\n    email: 'ephraim@blocks.so',\n    avatar: '/avatar-01.png',\n  },\n  navMain: [\n    {\n      id: 'overview',\n      title: 'Overview',\n      url: '#',\n      icon: LayoutDashboard,\n      isActive: true,\n    },\n    {\n      id: 'tasks',\n      title: 'Tasks',\n      url: '#',\n      icon: IconListDetails,\n    },\n    {\n      id: 'meetings',\n      title: 'Meetings',\n      url: '#',\n      icon: IconCalendarStats,\n    },\n    {\n      id: 'notes',\n      title: 'Notes',\n      url: '#',\n      icon: IconNotebook,\n    },\n    {\n      id: 'calendar',\n      title: 'Calendar',\n      url: '#',\n      icon: IconCalendar,\n    },\n    {\n      id: 'completed',\n      title: 'Completed',\n      url: '#',\n      icon: IconProgressCheck,\n    },\n    {\n      id: 'notifications',\n      title: 'Notifications',\n      url: '#',\n      icon: IconBellRinging,\n    },\n  ],\n  navCollapsible: {\n    favorites: [\n      {\n        id: 'design',\n        title: 'Design',\n        href: '#',\n        color: 'bg-green-400 dark:bg-green-300',\n      },\n      {\n        id: 'development',\n        title: 'Development',\n        href: '#',\n        color: 'bg-blue-400 dark:bg-blue-300',\n      },\n      {\n        id: 'workshop',\n        title: 'Workshop',\n        href: '#',\n        color: 'bg-orange-400 dark:bg-orange-300',\n      },\n      {\n        id: 'personal',\n        title: 'Personal',\n        href: '#',\n        color: 'bg-red-400 dark:bg-red-300',\n      },\n    ],\n    teams: [\n      {\n        id: 'engineering',\n        title: 'Engineering',\n        icon: IconSettingsCode,\n      },\n      {\n        id: 'marketing',\n        title: 'Marketing',\n        icon: IconAd2,\n      },\n    ],\n    topics: [\n      {\n        id: 'product-updates',\n        title: 'Product Updates',\n        icon: Package,\n      },\n      {\n        id: 'company-news',\n        title: 'Company News',\n        icon: IconNews,\n      },\n    ],\n  },\n};\n\nexport function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {\n  return (\n    <Sidebar {...props}>\n      <NavHeader data={data} />\n      <SidebarContent>\n        <NavMain items={data.navMain} />\n        <NavCollapsible\n          favorites={data.navCollapsible.favorites}\n          teams={data.navCollapsible.teams}\n          topics={data.navCollapsible.topics}\n        />\n      </SidebarContent>\n      <NavFooter user={data.user} />\n    </Sidebar>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-01/nav-collapsible.tsx",
          "type": "registry:component",
          "target": "components/sidebar-01/nav-collapsible.tsx",
          "content": "'use client';\nimport { ChevronDown } from 'lucide-react';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from '@/components/ui/collapsible';\nimport {\n  SidebarGroup,\n  SidebarGroupContent,\n  SidebarGroupLabel,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport type { FavoriteItem, TeamItem, TopicItem } from '@/components/sidebar-01/types';\n\ninterface NavCollapsibleProps {\n  favorites: FavoriteItem[];\n  teams: TeamItem[];\n  topics: TopicItem[];\n}\n\nexport function NavCollapsible({\n  favorites,\n  teams,\n  topics,\n}: NavCollapsibleProps) {\n  return (\n    <div className=\"space-y-0\">\n      {favorites && favorites.length > 0 && (\n        <Collapsible className=\"group/collapsible\" defaultOpen>\n          <SidebarGroup>\n            <SidebarGroupLabel\n              className=\"text-sm hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\"\n              render={<CollapsibleTrigger />}\n            >\n              Favorites\n              <ChevronDown className=\"ml-auto transition-transform group-data-open/collapsible:rotate-180\" />\n            </SidebarGroupLabel>\n            <CollapsibleContent>\n              <SidebarGroupContent>\n                <SidebarMenu>\n                  {favorites.map((item) => (\n                    <SidebarMenuItem key={item.id}>\n                      <SidebarMenuButton\n                        render={\n                          <a\n                            className=\"flex items-center gap-3\"\n                            href={item.href}\n                          />\n                        }\n                      >\n                        <div className={`h-3 w-3 rounded ${item.color}`} />\n                        <span>{item.title}</span>\n                      </SidebarMenuButton>\n                    </SidebarMenuItem>\n                  ))}\n                </SidebarMenu>\n              </SidebarGroupContent>\n            </CollapsibleContent>\n          </SidebarGroup>\n        </Collapsible>\n      )}\n\n      {[\n        { label: 'Teams', items: teams },\n        { label: 'Topics', items: topics },\n      ].map(\n        ({ label, items }) =>\n          items &&\n          items.length > 0 && (\n            <Collapsible className=\"group/collapsible\" key={label}>\n              <SidebarGroup>\n                <SidebarGroupLabel\n                  className=\"text-sm hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\"\n                  render={<CollapsibleTrigger />}\n                >\n                  {label}\n                  <ChevronDown className=\"ml-auto transition-transform group-data-open/collapsible:rotate-180\" />\n                </SidebarGroupLabel>\n                <CollapsibleContent>\n                  <SidebarGroupContent>\n                    <SidebarMenu>\n                      {items.map((item) => {\n                        const Icon = item.icon;\n                        return (\n                          <SidebarMenuItem key={item.id}>\n                            <SidebarMenuButton>\n                              <Icon className=\"mr-2 h-4 w-4\" />\n                              {item.title}\n                            </SidebarMenuButton>\n                          </SidebarMenuItem>\n                        );\n                      })}\n                    </SidebarMenu>\n                  </SidebarGroupContent>\n                </CollapsibleContent>\n              </SidebarGroup>\n            </Collapsible>\n          )\n      )}\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "sidebar"
      ]
    },
    {
      "name": "sidebar-02",
      "type": "registry:block",
      "title": "Sidebar Dashboard Inset",
      "description": "A sidebar dashboard inset block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "button",
        "collapsible",
        "dropdown-menu",
        "sidebar"
      ],
      "dependencies": [
        "framer-motion",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/sidebar/sidebar-02/nav-main.tsx",
          "type": "registry:component",
          "target": "components/sidebar-02/nav-main.tsx",
          "content": "'use client';\n\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport Link from 'next/link';\nimport type React from 'react';\nimport { useState } from 'react';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from '@/components/ui/collapsible';\nimport {\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  SidebarMenuSub,\n  SidebarMenuSubButton,\n  SidebarMenuItem as SidebarMenuSubItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\n\nexport type Route = {\n  id: string;\n  title: string;\n  icon?: React.ReactNode;\n  link: string;\n  subs?: {\n    title: string;\n    link: string;\n    icon?: React.ReactNode;\n  }[];\n};\n\nexport default function DashboardNavigation({ routes }: { routes: Route[] }) {\n  const { state } = useSidebar();\n  const isCollapsed = state === 'collapsed';\n  const [openCollapsible, setOpenCollapsible] = useState<string | null>(null);\n\n  return (\n    <SidebarMenu>\n      {routes.map((route) => {\n        const isOpen = !isCollapsed && openCollapsible === route.id;\n        const hasSubRoutes = !!route.subs?.length;\n\n        return (\n          <SidebarMenuItem key={route.id}>\n            {hasSubRoutes ? (\n              <Collapsible\n                className=\"w-full\"\n                onOpenChange={(open) =>\n                  setOpenCollapsible(open ? route.id : null)\n                }\n                open={isOpen}\n              >\n                <CollapsibleTrigger\n                  render={\n                    <SidebarMenuButton\n                      className={cn(\n                        'flex w-full items-center rounded-lg px-2 transition-colors',\n                        isOpen\n                          ? 'bg-sidebar-muted text-foreground'\n                          : 'text-muted-foreground hover:bg-sidebar-muted hover:text-foreground',\n                        isCollapsed && 'justify-center'\n                      )}\n                    />\n                  }\n                >\n                  {route.icon}\n                  {!isCollapsed && (\n                    <span className=\"ml-2 flex-1 font-medium text-sm\">\n                      {route.title}\n                    </span>\n                  )}\n                  {!isCollapsed && hasSubRoutes && (\n                    <span className=\"ml-auto\">\n                      {isOpen ? (\n                        <ChevronUp className=\"size-4\" />\n                      ) : (\n                        <ChevronDown className=\"size-4\" />\n                      )}\n                    </span>\n                  )}\n                </CollapsibleTrigger>\n\n                {!isCollapsed && (\n                  <CollapsibleContent>\n                    <SidebarMenuSub className=\"my-1 ml-3.5 \">\n                      {route.subs?.map((subRoute) => (\n                        <SidebarMenuSubItem\n                          className=\"h-auto\"\n                          key={`${route.id}-${subRoute.title}`}\n                        >\n                          <SidebarMenuSubButton\n                            render={\n                              <Link\n                                className=\"flex items-center rounded-md px-4 py-1.5 font-medium text-muted-foreground text-sm hover:bg-sidebar-muted hover:text-foreground\"\n                                href={subRoute.link}\n                                prefetch={true}\n                              />\n                            }\n                          >\n                            {subRoute.title}\n                          </SidebarMenuSubButton>\n                        </SidebarMenuSubItem>\n                      ))}\n                    </SidebarMenuSub>\n                  </CollapsibleContent>\n                )}\n              </Collapsible>\n            ) : (\n              <SidebarMenuButton\n                render={\n                  <Link\n                    className={cn(\n                      'flex items-center rounded-lg px-2 text-muted-foreground transition-colors hover:bg-sidebar-muted hover:text-foreground',\n                      isCollapsed && 'justify-center'\n                    )}\n                    href={route.link}\n                    prefetch={true}\n                  />\n                }\n                tooltip={route.title}\n              >\n                {route.icon}\n                {!isCollapsed && (\n                  <span className=\"ml-2 font-medium text-sm\">\n                    {route.title}\n                  </span>\n                )}\n              </SidebarMenuButton>\n            )}\n          </SidebarMenuItem>\n        );\n      })}\n    </SidebarMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-02/logo.tsx",
          "type": "registry:component",
          "target": "components/sidebar-02/logo.tsx",
          "content": "import type { SVGProps } from 'react';\n\nexport const Logo = (props: SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n"
        },
        {
          "path": "content/components/sidebar/sidebar-02/nav-notifications.tsx",
          "type": "registry:component",
          "target": "components/sidebar-02/nav-notifications.tsx",
          "content": "'use client';\n\nimport { BellIcon } from 'lucide-react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\n\ntype Notification = {\n  id: string;\n  avatar: string;\n  fallback: string;\n  text: string;\n  time: string;\n};\n\nexport function NotificationsPopover({\n  notifications,\n}: {\n  notifications: Notification[];\n}) {\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger\n        render={\n          <Button\n            aria-label=\"Open notifications\"\n            className=\"rounded-full\"\n            size=\"icon\"\n            variant=\"ghost\"\n          />\n        }\n      >\n        <BellIcon className=\"size-5\" />\n      </DropdownMenuTrigger>\n      <DropdownMenuContent className=\"my-6 w-80\" side=\"right\">\n        <DropdownMenuLabel>Notifications</DropdownMenuLabel>\n        <DropdownMenuSeparator />\n        {notifications.map(({ id, avatar, fallback, text, time }) => (\n          <DropdownMenuItem className=\"flex items-start gap-3\" key={id}>\n            <Avatar className=\"size-8\">\n              <AvatarImage alt=\"Avatar\" src={avatar} />\n              <AvatarFallback>{fallback}</AvatarFallback>\n            </Avatar>\n            <div className=\"flex flex-col\">\n              <span className=\"font-medium text-sm\">{text}</span>\n              <span className=\"text-muted-foreground text-xs\">{time}</span>\n            </div>\n          </DropdownMenuItem>\n        ))}\n        <DropdownMenuSeparator />\n        <DropdownMenuItem className=\"justify-center text-muted-foreground text-sm hover:text-primary\">\n          View all notifications\n        </DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-02/index.tsx",
          "type": "registry:component",
          "target": "components/sidebar-02/index.tsx",
          "content": "import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';\nimport { DashboardSidebar } from '@/components/sidebar-02/app-sidebar';\n\nexport default function Sidebar02() {\n  return (\n    <SidebarProvider>\n      <div className=\"relative flex h-dvh w-full\">\n        <DashboardSidebar />\n        <SidebarInset className=\"flex flex-col\" />\n      </div>\n    </SidebarProvider>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-02/team-switcher.tsx",
          "type": "registry:component",
          "target": "components/sidebar-02/team-switcher.tsx",
          "content": "'use client';\n\nimport { ChevronsUpDown, Plus } from 'lucide-react';\nimport * as React from 'react';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\n\ntype Team = {\n  name: string;\n  logo: React.ElementType;\n  plan: string;\n};\n\nexport function TeamSwitcher({ teams }: { teams: Team[] }) {\n  const { isMobile } = useSidebar();\n  const [activeTeam, setActiveTeam] = React.useState(teams[0]);\n\n  if (!activeTeam) return null;\n\n  const Logo = activeTeam.logo;\n\n  return (\n    <SidebarMenu>\n      <SidebarMenuItem>\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <SidebarMenuButton\n                className=\"data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground\"\n                size=\"lg\"\n              />\n            }\n          >\n            <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg bg-background text-foreground\">\n              <Logo className=\"size-4\" />\n            </div>\n            <div className=\"grid flex-1 text-left text-sm leading-tight\">\n              <span className=\"truncate font-semibold\">{activeTeam.name}</span>\n              <span className=\"truncate text-xs\">{activeTeam.plan}</span>\n            </div>\n            <ChevronsUpDown className=\"ml-auto\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"start\"\n            className=\"mb-4 w-(--anchor-width) min-w-56 rounded-lg\"\n            side={isMobile ? 'bottom' : 'right'}\n            sideOffset={4}\n          >\n            <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n              Teams\n            </DropdownMenuLabel>\n            {teams.map((team, index) => (\n              <DropdownMenuItem\n                className=\"gap-2 p-2\"\n                key={team.name}\n                onClick={() => setActiveTeam(team)}\n              >\n                <div className=\"flex size-6 items-center justify-center rounded-sm border\">\n                  <team.logo className=\"size-4 shrink-0\" />\n                </div>\n                {team.name}\n                <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>\n              </DropdownMenuItem>\n            ))}\n            <DropdownMenuSeparator />\n            <DropdownMenuItem className=\"gap-2 p-2\">\n              <div className=\"flex size-6 items-center justify-center rounded-md border bg-background\">\n                <Plus className=\"size-4\" />\n              </div>\n              <div className=\"font-medium text-muted-foreground\">Add team</div>\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </SidebarMenuItem>\n    </SidebarMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-02/app-sidebar.tsx",
          "type": "registry:component",
          "target": "components/sidebar-02/app-sidebar.tsx",
          "content": "'use client';\n\nimport { motion } from 'framer-motion';\nimport {\n  Activity,\n  DollarSign,\n  Home,\n  Infinity,\n  LinkIcon,\n  Package2,\n  Percent,\n  PieChart,\n  Settings,\n  ShoppingBag,\n  Sparkles,\n  Store,\n  TrendingUp,\n  Users,\n} from 'lucide-react';\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarHeader,\n  SidebarTrigger,\n  useSidebar,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport { Logo } from '@/components/sidebar-02/logo';\nimport type { Route } from '@/components/sidebar-02/nav-main';\nimport DashboardNavigation from '@/components/sidebar-02/nav-main';\nimport { NotificationsPopover } from '@/components/sidebar-02/nav-notifications';\nimport { TeamSwitcher } from '@/components/sidebar-02/team-switcher';\n\nconst sampleNotifications = [\n  {\n    id: '1',\n    avatar: '/avatars/01.png',\n    fallback: 'OM',\n    text: 'New order received.',\n    time: '10m ago',\n  },\n  {\n    id: '2',\n    avatar: '/avatars/02.png',\n    fallback: 'JL',\n    text: 'Server upgrade completed.',\n    time: '1h ago',\n  },\n  {\n    id: '3',\n    avatar: '/avatars/03.png',\n    fallback: 'HH',\n    text: 'New user signed up.',\n    time: '2h ago',\n  },\n];\n\nconst dashboardRoutes: Route[] = [\n  {\n    id: 'home',\n    title: 'Home',\n    icon: <Home className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'products',\n    title: 'Products',\n    icon: <Package2 className=\"size-4\" />,\n    link: '#',\n    subs: [\n      {\n        title: 'Catalogue',\n        link: '#',\n        icon: <Package2 className=\"size-4\" />,\n      },\n      {\n        title: 'Checkout Links',\n        link: '#',\n        icon: <LinkIcon className=\"size-4\" />,\n      },\n      {\n        title: 'Discounts',\n        link: '#',\n        icon: <Percent className=\"size-4\" />,\n      },\n    ],\n  },\n  {\n    id: 'usage-billing',\n    title: 'Usage Billing',\n    icon: <PieChart className=\"size-4\" />,\n    link: '#',\n    subs: [\n      {\n        title: 'Meters',\n        link: '#',\n        icon: <PieChart className=\"size-4\" />,\n      },\n      {\n        title: 'Events',\n        link: '#',\n        icon: <Activity className=\"size-4\" />,\n      },\n    ],\n  },\n  {\n    id: 'benefits',\n    title: 'Benefits',\n    icon: <Sparkles className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'customers',\n    title: 'Customers',\n    icon: <Users className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'sales',\n    title: 'Sales',\n    icon: <ShoppingBag className=\"size-4\" />,\n    link: '#',\n    subs: [\n      {\n        title: 'Orders',\n        link: '#',\n        icon: <ShoppingBag className=\"size-4\" />,\n      },\n      {\n        title: 'Subscriptions',\n        link: '#',\n        icon: <Infinity className=\"size-4\" />,\n      },\n    ],\n  },\n  {\n    id: 'storefront',\n    title: 'Storefront',\n    icon: <Store className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'analytics',\n    title: 'Analytics',\n    icon: <TrendingUp className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'finance',\n    title: 'Finance',\n    icon: <DollarSign className=\"size-4\" />,\n    link: '#',\n    subs: [\n      { title: 'Incoming', link: '#' },\n      { title: 'Outgoing', link: '#' },\n      { title: 'Payout Account', link: '#' },\n    ],\n  },\n  {\n    id: 'settings',\n    title: 'Settings',\n    icon: <Settings className=\"size-4\" />,\n    link: '#',\n    subs: [\n      { title: 'General', link: '#' },\n      { title: 'Webhooks', link: '#' },\n      { title: 'Custom Fields', link: '#' },\n    ],\n  },\n];\n\nconst teams = [\n  { id: '1', name: 'Alpha Inc.', logo: Logo, plan: 'Free' },\n  { id: '2', name: 'Beta Corp.', logo: Logo, plan: 'Free' },\n  { id: '3', name: 'Gamma Tech', logo: Logo, plan: 'Free' },\n];\n\nexport function DashboardSidebar() {\n  const { state } = useSidebar();\n  const isCollapsed = state === 'collapsed';\n\n  return (\n    <Sidebar collapsible=\"icon\" variant=\"inset\">\n      <SidebarHeader\n        className={cn(\n          'flex md:pt-3.5',\n          isCollapsed\n            ? 'flex-row items-center justify-between gap-y-4 md:flex-col md:items-start md:justify-start'\n            : 'flex-row items-center justify-between'\n        )}\n      >\n        <a className=\"flex items-center gap-2\" href=\"#\">\n          <Logo className=\"h-8 w-8\" />\n          {!isCollapsed && (\n            <span className=\"font-semibold text-black dark:text-white\">\n              Acme\n            </span>\n          )}\n        </a>\n\n        <motion.div\n          animate={{ opacity: 1 }}\n          className={cn(\n            'flex items-center gap-2',\n            isCollapsed ? 'flex-row md:flex-col-reverse' : 'flex-row'\n          )}\n          initial={{ opacity: 0 }}\n          key={isCollapsed ? 'header-collapsed' : 'header-expanded'}\n          transition={{ duration: 0.8 }}\n        >\n          <NotificationsPopover notifications={sampleNotifications} />\n          <SidebarTrigger />\n        </motion.div>\n      </SidebarHeader>\n      <SidebarContent className=\"gap-4 px-2 py-4\">\n        <DashboardNavigation routes={dashboardRoutes} />\n      </SidebarContent>\n      <SidebarFooter className=\"px-2\">\n        <TeamSwitcher teams={teams} />\n      </SidebarFooter>\n    </Sidebar>\n  );\n}\n"
        }
      ],
      "categories": [
        "sidebar"
      ]
    },
    {
      "name": "sidebar-03",
      "type": "registry:block",
      "title": "Sidebar Dashboard Floating",
      "description": "A sidebar dashboard floating block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "button",
        "collapsible",
        "dropdown-menu",
        "sidebar"
      ],
      "dependencies": [
        "framer-motion",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/sidebar/sidebar-03/nav-main.tsx",
          "type": "registry:component",
          "target": "components/sidebar-03/nav-main.tsx",
          "content": "'use client';\n\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport Link from 'next/link';\nimport type React from 'react';\nimport { useState } from 'react';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from '@/components/ui/collapsible';\nimport {\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  SidebarMenuSub,\n  SidebarMenuSubButton,\n  SidebarMenuItem as SidebarMenuSubItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\n\nexport type Route = {\n  id: string;\n  title: string;\n  icon?: React.ReactNode;\n  link: string;\n  subs?: {\n    title: string;\n    link: string;\n    icon?: React.ReactNode;\n  }[];\n};\n\nexport default function DashboardNavigation({ routes }: { routes: Route[] }) {\n  const { state } = useSidebar();\n  const isCollapsed = state === 'collapsed';\n  const [openCollapsible, setOpenCollapsible] = useState<string | null>(null);\n\n  return (\n    <SidebarMenu>\n      {routes.map((route) => {\n        const isOpen = !isCollapsed && openCollapsible === route.id;\n        const hasSubRoutes = !!route.subs?.length;\n\n        return (\n          <SidebarMenuItem key={route.id}>\n            {hasSubRoutes ? (\n              <Collapsible\n                className=\"w-full\"\n                onOpenChange={(open) =>\n                  setOpenCollapsible(open ? route.id : null)\n                }\n                open={isOpen}\n              >\n                <CollapsibleTrigger\n                  render={\n                    <SidebarMenuButton\n                      className={cn(\n                        'flex w-full items-center rounded-lg px-2 transition-colors',\n                        isOpen\n                          ? 'bg-sidebar-muted text-foreground'\n                          : 'text-muted-foreground hover:bg-sidebar-muted hover:text-foreground',\n                        isCollapsed && 'justify-center'\n                      )}\n                    />\n                  }\n                >\n                  {route.icon}\n                  {!isCollapsed && (\n                    <span className=\"ml-2 flex-1 font-medium text-sm\">\n                      {route.title}\n                    </span>\n                  )}\n                  {!isCollapsed && hasSubRoutes && (\n                    <span className=\"ml-auto\">\n                      {isOpen ? (\n                        <ChevronUp className=\"size-4\" />\n                      ) : (\n                        <ChevronDown className=\"size-4\" />\n                      )}\n                    </span>\n                  )}\n                </CollapsibleTrigger>\n\n                {!isCollapsed && (\n                  <CollapsibleContent>\n                    <SidebarMenuSub className=\"my-1 ml-3.5 \">\n                      {route.subs?.map((subRoute) => (\n                        <SidebarMenuSubItem\n                          className=\"h-auto\"\n                          key={`${route.id}-${subRoute.title}`}\n                        >\n                          <SidebarMenuSubButton\n                            render={\n                              <Link\n                                className=\"flex items-center rounded-md px-4 py-1.5 font-medium text-muted-foreground text-sm hover:bg-sidebar-muted hover:text-foreground\"\n                                href={subRoute.link}\n                                prefetch={true}\n                              />\n                            }\n                          >\n                            {subRoute.title}\n                          </SidebarMenuSubButton>\n                        </SidebarMenuSubItem>\n                      ))}\n                    </SidebarMenuSub>\n                  </CollapsibleContent>\n                )}\n              </Collapsible>\n            ) : (\n              <SidebarMenuButton\n                render={\n                  <Link\n                    className={cn(\n                      'flex items-center rounded-lg px-2 text-muted-foreground transition-colors hover:bg-sidebar-muted hover:text-foreground',\n                      isCollapsed && 'justify-center'\n                    )}\n                    href={route.link}\n                    prefetch={true}\n                  />\n                }\n                tooltip={route.title}\n              >\n                {route.icon}\n                {!isCollapsed && (\n                  <span className=\"ml-2 font-medium text-sm\">\n                    {route.title}\n                  </span>\n                )}\n              </SidebarMenuButton>\n            )}\n          </SidebarMenuItem>\n        );\n      })}\n    </SidebarMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-03/logo.tsx",
          "type": "registry:component",
          "target": "components/sidebar-03/logo.tsx",
          "content": "import type { SVGProps } from 'react';\n\nexport const Logo = (props: SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"currentColor\"\n    height=\"48\"\n    viewBox=\"0 0 40 48\"\n    width=\"40\"\n    {...props}\n  >\n    <title>Logo</title>\n    <clipPath id=\"a\">\n      <path d=\"m0 0h40v48h-40z\" />\n    </clipPath>\n    <g clipPath=\"url(#a)\">\n      <path d=\"m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z\" />\n      <path d=\"m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z\" />\n      <path d=\"m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z\" />\n      <path d=\"m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z\" />\n      <path d=\"m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z\" />\n      <path d=\"m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z\" />\n    </g>\n  </svg>\n);\n"
        },
        {
          "path": "content/components/sidebar/sidebar-03/nav-notifications.tsx",
          "type": "registry:component",
          "target": "components/sidebar-03/nav-notifications.tsx",
          "content": "'use client';\n\nimport { BellIcon } from 'lucide-react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Button } from '@/components/ui/button';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\n\ntype Notification = {\n  id: string;\n  avatar: string;\n  fallback: string;\n  text: string;\n  time: string;\n};\n\nexport function NotificationsPopover({\n  notifications,\n}: {\n  notifications: Notification[];\n}) {\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger\n        render={\n          <Button\n            aria-label=\"Open notifications\"\n            className=\"rounded-full\"\n            size=\"icon\"\n            variant=\"ghost\"\n          />\n        }\n      >\n        <BellIcon className=\"size-5\" />\n      </DropdownMenuTrigger>\n      <DropdownMenuContent className=\"my-6 w-80\" side=\"right\">\n        <DropdownMenuLabel>Notifications</DropdownMenuLabel>\n        <DropdownMenuSeparator />\n        {notifications.map(({ id, avatar, fallback, text, time }) => (\n          <DropdownMenuItem className=\"flex items-start gap-3\" key={id}>\n            <Avatar className=\"size-8\">\n              <AvatarImage alt=\"Avatar\" src={avatar} />\n              <AvatarFallback>{fallback}</AvatarFallback>\n            </Avatar>\n            <div className=\"flex flex-col\">\n              <span className=\"font-medium text-sm\">{text}</span>\n              <span className=\"text-muted-foreground text-xs\">{time}</span>\n            </div>\n          </DropdownMenuItem>\n        ))}\n        <DropdownMenuSeparator />\n        <DropdownMenuItem className=\"justify-center text-muted-foreground text-sm hover:text-primary\">\n          View all notifications\n        </DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-03/index.tsx",
          "type": "registry:component",
          "target": "components/sidebar-03/index.tsx",
          "content": "import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';\nimport { DashboardSidebar } from '@/components/sidebar-03/app-sidebar';\n\nexport default function Sidebar03() {\n  return (\n    <SidebarProvider>\n      <div className=\"relative flex h-dvh w-full\">\n        <DashboardSidebar />\n        <SidebarInset className=\"flex flex-col\" />\n      </div>\n    </SidebarProvider>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-03/team-switcher.tsx",
          "type": "registry:component",
          "target": "components/sidebar-03/team-switcher.tsx",
          "content": "'use client';\n\nimport { ChevronsUpDown, Plus } from 'lucide-react';\nimport * as React from 'react';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\n\ntype Team = {\n  name: string;\n  logo: React.ElementType;\n  plan: string;\n};\n\nexport function TeamSwitcher({ teams }: { teams: Team[] }) {\n  const { isMobile } = useSidebar();\n  const [activeTeam, setActiveTeam] = React.useState(teams[0]);\n\n  if (!activeTeam) return null;\n\n  const Logo = activeTeam.logo;\n\n  return (\n    <SidebarMenu>\n      <SidebarMenuItem>\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <SidebarMenuButton\n                className=\"data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground\"\n                size=\"lg\"\n              />\n            }\n          >\n            <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg bg-background text-foreground\">\n              <Logo className=\"size-4\" />\n            </div>\n            <div className=\"grid flex-1 text-left text-sm leading-tight\">\n              <span className=\"truncate font-semibold\">{activeTeam.name}</span>\n              <span className=\"truncate text-xs\">{activeTeam.plan}</span>\n            </div>\n            <ChevronsUpDown className=\"ml-auto\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"start\"\n            className=\"mb-4 w-(--anchor-width) min-w-56 rounded-lg\"\n            side={isMobile ? 'bottom' : 'right'}\n            sideOffset={4}\n          >\n            <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n              Teams\n            </DropdownMenuLabel>\n            {teams.map((team, index) => (\n              <DropdownMenuItem\n                className=\"gap-2 p-2\"\n                key={team.name}\n                onClick={() => setActiveTeam(team)}\n              >\n                <div className=\"flex size-6 items-center justify-center rounded-sm border\">\n                  <team.logo className=\"size-4 shrink-0\" />\n                </div>\n                {team.name}\n                <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>\n              </DropdownMenuItem>\n            ))}\n            <DropdownMenuSeparator />\n            <DropdownMenuItem className=\"gap-2 p-2\">\n              <div className=\"flex size-6 items-center justify-center rounded-md border bg-background\">\n                <Plus className=\"size-4\" />\n              </div>\n              <div className=\"font-medium text-muted-foreground\">Add team</div>\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </SidebarMenuItem>\n    </SidebarMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-03/app-sidebar.tsx",
          "type": "registry:component",
          "target": "components/sidebar-03/app-sidebar.tsx",
          "content": "'use client';\n\nimport { motion } from 'framer-motion';\nimport {\n  Activity,\n  DollarSign,\n  Home,\n  Infinity,\n  LinkIcon,\n  Package2,\n  Percent,\n  PieChart,\n  Settings,\n  ShoppingBag,\n  Sparkles,\n  Store,\n  TrendingUp,\n  Users,\n} from 'lucide-react';\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarHeader,\n  SidebarTrigger,\n  useSidebar,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport { Logo } from '@/components/sidebar-03/logo';\nimport type { Route } from '@/components/sidebar-03/nav-main';\nimport DashboardNavigation from '@/components/sidebar-03/nav-main';\nimport { NotificationsPopover } from '@/components/sidebar-03/nav-notifications';\nimport { TeamSwitcher } from '@/components/sidebar-03/team-switcher';\n\nconst sampleNotifications = [\n  {\n    id: '1',\n    avatar: '/avatars/01.png',\n    fallback: 'OM',\n    text: 'New order received.',\n    time: '10m ago',\n  },\n  {\n    id: '2',\n    avatar: '/avatars/02.png',\n    fallback: 'JL',\n    text: 'Server upgrade completed.',\n    time: '1h ago',\n  },\n  {\n    id: '3',\n    avatar: '/avatars/03.png',\n    fallback: 'HH',\n    text: 'New user signed up.',\n    time: '2h ago',\n  },\n];\n\nconst dashboardRoutes: Route[] = [\n  {\n    id: 'home',\n    title: 'Home',\n    icon: <Home className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'products',\n    title: 'Products',\n    icon: <Package2 className=\"size-4\" />,\n    link: '#',\n    subs: [\n      {\n        title: 'Catalogue',\n        link: '#',\n        icon: <Package2 className=\"size-4\" />,\n      },\n      {\n        title: 'Checkout Links',\n        link: '#',\n        icon: <LinkIcon className=\"size-4\" />,\n      },\n      {\n        title: 'Discounts',\n        link: '#',\n        icon: <Percent className=\"size-4\" />,\n      },\n    ],\n  },\n  {\n    id: 'usage-billing',\n    title: 'Usage Billing',\n    icon: <PieChart className=\"size-4\" />,\n    link: '#',\n    subs: [\n      {\n        title: 'Meters',\n        link: '#',\n        icon: <PieChart className=\"size-4\" />,\n      },\n      {\n        title: 'Events',\n        link: '#',\n        icon: <Activity className=\"size-4\" />,\n      },\n    ],\n  },\n  {\n    id: 'benefits',\n    title: 'Benefits',\n    icon: <Sparkles className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'customers',\n    title: 'Customers',\n    icon: <Users className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'sales',\n    title: 'Sales',\n    icon: <ShoppingBag className=\"size-4\" />,\n    link: '#',\n    subs: [\n      {\n        title: 'Orders',\n        link: '#',\n        icon: <ShoppingBag className=\"size-4\" />,\n      },\n      {\n        title: 'Subscriptions',\n        link: '#',\n        icon: <Infinity className=\"size-4\" />,\n      },\n    ],\n  },\n  {\n    id: 'storefront',\n    title: 'Storefront',\n    icon: <Store className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'analytics',\n    title: 'Analytics',\n    icon: <TrendingUp className=\"size-4\" />,\n    link: '#',\n  },\n  {\n    id: 'finance',\n    title: 'Finance',\n    icon: <DollarSign className=\"size-4\" />,\n    link: '#',\n    subs: [\n      { title: 'Incoming', link: '#' },\n      { title: 'Outgoing', link: '#' },\n      { title: 'Payout Account', link: '#' },\n    ],\n  },\n  {\n    id: 'settings',\n    title: 'Settings',\n    icon: <Settings className=\"size-4\" />,\n    link: '#',\n    subs: [\n      { title: 'General', link: '#' },\n      { title: 'Webhooks', link: '#' },\n      { title: 'Custom Fields', link: '#' },\n    ],\n  },\n];\n\nconst teams = [\n  { id: '1', name: 'Alpha Inc.', logo: Logo, plan: 'Free' },\n  { id: '2', name: 'Beta Corp.', logo: Logo, plan: 'Free' },\n  { id: '3', name: 'Gamma Tech', logo: Logo, plan: 'Free' },\n];\n\nexport function DashboardSidebar() {\n  const { state } = useSidebar();\n  const isCollapsed = state === 'collapsed';\n\n  return (\n    <Sidebar collapsible=\"icon\" variant=\"floating\">\n      <SidebarHeader\n        className={cn(\n          'flex md:pt-3.5',\n          isCollapsed\n            ? 'flex-row items-center justify-between gap-y-4 md:flex-col md:items-start md:justify-start'\n            : 'flex-row items-center justify-between'\n        )}\n      >\n        <a className=\"flex items-center gap-2\" href=\"#\">\n          <Logo className=\"h-8 w-8\" />\n          {!isCollapsed && (\n            <span className=\"font-semibold text-black dark:text-white\">\n              Acme\n            </span>\n          )}\n        </a>\n\n        <motion.div\n          animate={{ opacity: 1 }}\n          className={cn(\n            'flex items-center gap-2',\n            isCollapsed ? 'flex-row md:flex-col-reverse' : 'flex-row'\n          )}\n          initial={{ opacity: 0 }}\n          key={isCollapsed ? 'header-collapsed' : 'header-expanded'}\n          transition={{ duration: 0.8 }}\n        >\n          <NotificationsPopover notifications={sampleNotifications} />\n          <SidebarTrigger />\n        </motion.div>\n      </SidebarHeader>\n      <SidebarContent className=\"gap-4 px-2 py-4\">\n        <DashboardNavigation routes={dashboardRoutes} />\n      </SidebarContent>\n      <SidebarFooter className=\"px-2\">\n        <TeamSwitcher teams={teams} />\n      </SidebarFooter>\n    </Sidebar>\n  );\n}\n"
        }
      ],
      "categories": [
        "sidebar"
      ]
    },
    {
      "name": "sidebar-04",
      "type": "registry:block",
      "title": "Sidebar Double-Sided Mail",
      "description": "A sidebar double-sided mail block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "dropdown-menu",
        "sidebar"
      ],
      "dependencies": [
        "@tabler/icons-react",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/sidebar/sidebar-04/mail-context.tsx",
          "type": "registry:component",
          "target": "components/sidebar-04/mail-context.tsx",
          "content": "'use client';\n\nimport * as React from 'react';\n\nexport type Mail = {\n  name: string;\n  email: string;\n  subject: string;\n  date: string;\n  teaser: string;\n  labels?: string[];\n};\n\ntype MailContextValue = {\n  selectedMail: Mail | null;\n  setSelectedMail: (mail: Mail | null) => void;\n};\n\nconst MailContext = React.createContext<MailContextValue | undefined>(\n  undefined\n);\n\nexport function MailProvider({ children }: { children: React.ReactNode }) {\n  const [selectedMail, setSelectedMail] = React.useState<Mail | null>(null);\n  const value = React.useMemo(\n    () => ({ selectedMail, setSelectedMail }),\n    [selectedMail]\n  );\n  return <MailContext.Provider value={value}>{children}</MailContext.Provider>;\n}\n\nexport function useMail() {\n  const ctx = React.useContext(MailContext);\n  if (!ctx) throw new Error('useMail must be used within MailProvider');\n  return ctx;\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-04/app-sidebar.tsx",
          "type": "registry:component",
          "target": "components/sidebar-04/app-sidebar.tsx",
          "content": "'use client';\n\nimport {\n  IconBrandAmongUs,\n  IconCarambola,\n  IconHourglassHigh,\n  IconMailbox,\n} from '@tabler/icons-react';\nimport { Archive, Flag } from 'lucide-react';\nimport * as React from 'react';\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupContent,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarInput,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\nimport { useMail } from '@/components/sidebar-04/mail-context';\nimport { NavUser } from '@/components/sidebar-04/nav-user';\n\n// This is sample data\nconst data = {\n  user: {\n    name: 'ephraim',\n    email: 'ephraim@blocks.so',\n    avatar: '/avatar-01.png',\n  },\n  navMain: [\n    {\n      title: 'Inbox',\n      url: '#',\n      icon: IconMailbox,\n      isActive: true,\n    },\n    {\n      title: 'Starred',\n      url: '#',\n      icon: IconCarambola,\n      isActive: false,\n    },\n    {\n      title: 'Important',\n      url: '#',\n      icon: Flag,\n      isActive: false,\n    },\n    {\n      title: 'Scheduled',\n      url: '#',\n      icon: IconHourglassHigh,\n      isActive: false,\n    },\n    {\n      title: 'Archive',\n      url: '#',\n      icon: Archive,\n      isActive: false,\n    },\n  ],\n  labels: [\n    { title: 'Personal', color: 'bg-green-400 dark:bg-green-300' },\n    { title: 'Work', color: 'bg-blue-400 dark:bg-blue-300' },\n    { title: 'Travel', color: 'bg-orange-400 dark:bg-orange-300' },\n    { title: 'Receipts', color: 'bg-purple-400 dark:bg-purple-300' },\n  ],\n  mails: [\n    {\n      name: 'Nora Patel',\n      email: 'nora@acme.co',\n      subject: 'Welcome to Acme Mail',\n      date: '08:15 AM',\n      teaser:\n        \"Hi there — here's a quick tour of your new inbox.\\nPin, label, and schedule messages to stay organized.\",\n    },\n    {\n      name: 'Stripe',\n      email: 'no-reply@stripe.com',\n      subject: 'Your payout has arrived',\n      date: 'Yesterday',\n      teaser:\n        'A payout of $3,245.90 was sent to your bank account.\\nView details in your dashboard.',\n    },\n    {\n      name: 'GitHub',\n      email: 'noreply@github.com',\n      subject: 'New activity on acme/app',\n      date: 'Tue',\n      teaser:\n        '3 pull requests need your review. CI passed on main.\\nClick to open the review queue.',\n    },\n    {\n      name: 'Ava Chen',\n      email: 'ava.chen@example.com',\n      subject: 'Agenda for Friday standup',\n      date: 'Mon',\n      teaser:\n        \"Let's cover onboarding, billing bugs, and Q4 goals.\\nReply with anything you want to add.\",\n    },\n    {\n      name: 'Figma',\n      email: 'updates@figma.com',\n      subject: \"What's new in Figma\",\n      date: 'Sep 12',\n      teaser:\n        'Variables, auto layout improvements, and dev mode updates.\\nWatch the recap to learn more.',\n    },\n    {\n      name: 'Linear',\n      email: 'bot@linear.app',\n      subject: '[ACME-432] Edit modal broken',\n      date: 'Sep 11',\n      teaser:\n        'Issue created by Wendy. Repro steps included.\\nSeverity: high, priority: P1.',\n    },\n    {\n      name: 'Airbnb',\n      email: 'booking@airbnb.com',\n      subject: 'Your trip to Kyoto',\n      date: 'Sep 10',\n      teaser:\n        'Get ready for your stay. Check-in details and local recommendations inside.',\n    },\n    {\n      name: 'Notion',\n      email: 'team@notion.so',\n      subject: 'Weekly team recap',\n      date: 'Sep 09',\n      teaser:\n        'Marketing shipped pricing page revamp. Eng closed 14 issues.\\nSee full notes in the doc.',\n    },\n    {\n      name: 'Google Calendar',\n      email: 'calendar@google.com',\n      subject: 'Design sync moved',\n      date: 'Sep 09',\n      teaser:\n        \"Event 'Design sync' moved to 3:30 PM. Meet link updated in the invite.\",\n    },\n    {\n      name: 'HN Daily',\n      email: 'digest@ycombinator.com',\n      subject: 'Top stories today',\n      date: 'Sep 08',\n      teaser:\n        'OpenAI releases new API, SQLite 3.46, and why we left Kubernetes.\\nRead the full digest.',\n    },\n  ],\n};\n\nexport function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {\n  // Note: I'm using state to show active item.\n  // IRL you should use the url/router.\n  const [activeItem, setActiveItem] = React.useState(data.navMain[0]);\n  const [mails, setMails] = React.useState(data.mails);\n  const [query, setQuery] = React.useState('');\n  const { setOpen } = useSidebar();\n  const { setSelectedMail } = useMail();\n\n  const filteredMails = React.useMemo(() => {\n    const q = query.trim().toLowerCase();\n    if (!q) return mails;\n    return mails.filter((m) =>\n      [m.name, m.email, m.subject, m.teaser]\n        .join('\\n')\n        .toLowerCase()\n        .includes(q)\n    );\n  }, [mails, query]);\n\n  return (\n    <div className=\"flex\">\n      {/* This is the first sidebar */}\n      <Sidebar\n        className=\"border-r p-2 px-1\"\n        collapsible=\"none\"\n        style={{ '--sidebar-width': '12rem' } as React.CSSProperties}\n      >\n        <SidebarHeader>\n          <SidebarMenu>\n            <SidebarMenuItem>\n              <SidebarMenuButton\n                className=\"md:h-8 md:p-0\"\n                render={<a href=\"#\" />}\n                size=\"lg\"\n              >\n                <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-accent text-sidebar-accent-foreground\">\n                  <IconBrandAmongUs className=\"size-4\" />\n                </div>\n                <div className=\"grid flex-1 text-left text-sm leading-tight\">\n                  <span className=\"truncate font-medium\">Acme Inc</span>\n                  <span className=\"truncate text-xs\">Enterprise</span>\n                </div>\n              </SidebarMenuButton>\n            </SidebarMenuItem>\n          </SidebarMenu>\n        </SidebarHeader>\n        <SidebarContent>\n          <SidebarGroup>\n            <SidebarGroupContent className=\"px-1.5 md:px-0\">\n              <SidebarMenu>\n                {data.navMain.map((item) => (\n                  <SidebarMenuItem key={item.title}>\n                    <SidebarMenuButton\n                      className=\"px-2.5 md:px-2\"\n                      isActive={activeItem?.title === item.title}\n                      onClick={() => {\n                        setActiveItem(item);\n                        const mail = data.mails.sort(() => Math.random() - 0.5);\n                        setMails(\n                          mail.slice(\n                            0,\n                            Math.max(5, Math.floor(Math.random() * 10) + 1)\n                          )\n                        );\n                        setOpen(true);\n                      }}\n                    >\n                      <item.icon />\n                      <span>{item.title}</span>\n                    </SidebarMenuButton>\n                  </SidebarMenuItem>\n                ))}\n              </SidebarMenu>\n            </SidebarGroupContent>\n          </SidebarGroup>\n          <SidebarGroup>\n            <SidebarGroupLabel className=\"px-1.5 text-xs md:px-0\">\n              Labels\n            </SidebarGroupLabel>\n            <SidebarGroupContent className=\"px-1.5 md:px-0\">\n              <SidebarMenu>\n                {data.labels.map((label) => (\n                  <SidebarMenuItem key={label.title}>\n                    <SidebarMenuButton\n                      className=\"px-2.5 md:px-2\"\n                      render={<div className=\"flex items-center gap-3\" />}\n                    >\n                      <div className={`h-3 w-3 rounded ${label.color}`} />\n                      <span>{label.title}</span>\n                    </SidebarMenuButton>\n                  </SidebarMenuItem>\n                ))}\n              </SidebarMenu>\n            </SidebarGroupContent>\n          </SidebarGroup>\n        </SidebarContent>\n        <SidebarFooter>\n          <NavUser user={data.user} />\n        </SidebarFooter>\n      </Sidebar>\n\n      {/* This is the second sidebar */}\n      {/* We disable collapsible and let it fill remaining space */}\n      <Sidebar\n        className=\"hidden min-w-96 flex-1 border-r md:flex\"\n        collapsible=\"none\"\n      >\n        <SidebarHeader className=\"gap-3.5 border-b p-4\">\n          <div className=\"flex w-full items-center justify-between\">\n            <div className=\"font-medium text-base text-foreground\">\n              {activeItem?.title}\n            </div>\n          </div>\n          <SidebarInput\n            onChange={(e) => setQuery(e.target.value)}\n            placeholder=\"Type to search...\"\n            value={query}\n          />\n        </SidebarHeader>\n        <SidebarContent>\n          <SidebarGroup className=\"px-0 pt-0\">\n            <SidebarGroupContent>\n              {filteredMails.length === 0 && (\n                <div className=\"p-4 text-muted-foreground text-sm\">\n                  No results\n                </div>\n              )}\n              {filteredMails.map((mail) => (\n                <a\n                  className=\"flex flex-col items-start gap-2 whitespace-nowrap border-b p-4 text-sm leading-tight hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\"\n                  href=\"#\"\n                  key={mail.email}\n                  onClick={(e) => {\n                    e.preventDefault();\n                    setSelectedMail(mail);\n                  }}\n                >\n                  <div className=\"flex w-full items-center gap-2\">\n                    <span>{mail.name}</span>{' '}\n                    <span className=\"ml-auto text-xs\">{mail.date}</span>\n                  </div>\n                  <span className=\"font-medium\">{mail.subject}</span>\n                  <span className=\"line-clamp-2 w-[260px] whitespace-break-spaces text-xs\">\n                    {mail.teaser}\n                  </span>\n                </a>\n              ))}\n            </SidebarGroupContent>\n          </SidebarGroup>\n        </SidebarContent>\n      </Sidebar>\n    </div>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-04/nav-user.tsx",
          "type": "registry:component",
          "target": "components/sidebar-04/nav-user.tsx",
          "content": "'use client';\n\nimport {\n  BadgeCheck,\n  Bell,\n  ChevronsUpDown,\n  CreditCard,\n  LogOut,\n  Sparkles,\n} from 'lucide-react';\n\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\n\nexport function NavUser({\n  user,\n}: {\n  user: {\n    name: string;\n    email: string;\n    avatar: string;\n  };\n}) {\n  const { isMobile } = useSidebar();\n\n  return (\n    <SidebarMenu>\n      <SidebarMenuItem>\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <SidebarMenuButton\n                className=\"data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground md:h-8 md:p-0\"\n                size=\"lg\"\n              />\n            }\n          >\n            <Avatar className=\"h-8 w-8 rounded-lg\">\n              <AvatarImage alt={user.name} src={user.avatar} />\n              <AvatarFallback className=\"rounded-lg\">CN</AvatarFallback>\n            </Avatar>\n            <div className=\"grid flex-1 text-left text-sm leading-tight\">\n              <span className=\"truncate font-medium\">{user.name}</span>\n              <span className=\"truncate text-xs\">{user.email}</span>\n            </div>\n            <ChevronsUpDown className=\"ml-auto size-4\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"end\"\n            className=\"w-(--anchor-width) min-w-56 rounded-lg\"\n            side={isMobile ? 'bottom' : 'right'}\n            sideOffset={4}\n          >\n            <DropdownMenuLabel className=\"p-0 font-normal\">\n              <div className=\"flex items-center gap-2 px-1 py-1.5 text-left text-sm\">\n                <Avatar className=\"h-8 w-8 rounded-lg\">\n                  <AvatarImage alt={user.name} src={user.avatar} />\n                  <AvatarFallback className=\"rounded-lg\">CN</AvatarFallback>\n                </Avatar>\n                <div className=\"grid flex-1 text-left text-sm leading-tight\">\n                  <span className=\"truncate font-medium\">{user.name}</span>\n                  <span className=\"truncate text-xs\">{user.email}</span>\n                </div>\n              </div>\n            </DropdownMenuLabel>\n            <DropdownMenuSeparator />\n            <DropdownMenuGroup>\n              <DropdownMenuItem>\n                <Sparkles />\n                Upgrade to Pro\n              </DropdownMenuItem>\n            </DropdownMenuGroup>\n            <DropdownMenuSeparator />\n            <DropdownMenuGroup>\n              <DropdownMenuItem>\n                <BadgeCheck />\n                Account\n              </DropdownMenuItem>\n              <DropdownMenuItem>\n                <CreditCard />\n                Billing\n              </DropdownMenuItem>\n              <DropdownMenuItem>\n                <Bell />\n                Notifications\n              </DropdownMenuItem>\n            </DropdownMenuGroup>\n            <DropdownMenuSeparator />\n            <DropdownMenuItem>\n              <LogOut />\n              Log out\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </SidebarMenuItem>\n    </SidebarMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-04/app/page.tsx",
          "type": "registry:page",
          "target": "app/page.tsx",
          "content": "'use client';\n\nimport { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';\nimport { AppSidebar } from '@/components/sidebar-04/app-sidebar';\nimport { MailProvider, useMail } from '@/components/sidebar-04/mail-context';\n\nfunction MailPreview() {\n  const { selectedMail } = useMail();\n  if (!selectedMail) {\n    return (\n      <div className=\"flex h-full items-center justify-center text-muted-foreground text-sm\">\n        Select an email to preview\n      </div>\n    );\n  }\n  return (\n    <div className=\"flex h-full flex-col\">\n      <div className=\"border-b p-4\">\n        <div className=\"font-semibold text-lg\">{selectedMail.subject}</div>\n        <div className=\"text-muted-foreground text-sm\">\n          From: {selectedMail.name} ({selectedMail.email})\n        </div>\n      </div>\n      <div className=\"whitespace-pre-wrap p-4 text-sm\">{`${selectedMail.teaser}\\n\\nThis is a sample message body for the selected email.\\nIt demonstrates the preview area on the right side.`}</div>\n    </div>\n  );\n}\n\nexport default function Page() {\n  return (\n    <SidebarProvider>\n      <MailProvider>\n        <div className=\"flex h-dvh w-full\">\n          <AppSidebar />\n          <SidebarInset>\n            <div className=\"m-2 flex-1 rounded-xl border\">\n              <MailPreview />\n            </div>\n          </SidebarInset>\n        </div>\n      </MailProvider>\n    </SidebarProvider>\n  );\n}\n"
        }
      ],
      "categories": [
        "sidebar"
      ]
    },
    {
      "name": "sidebar-05",
      "type": "registry:block",
      "title": "Sidebar Double-Sided",
      "description": "A sidebar double-sided block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "dropdown-menu",
        "sidebar"
      ],
      "dependencies": [
        "@tabler/icons-react",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/sidebar/sidebar-05/team-switcher.tsx",
          "type": "registry:component",
          "target": "components/sidebar-05/team-switcher.tsx",
          "content": "'use client';\n\nimport { ChevronsUpDown, Plus } from 'lucide-react';\nimport * as React from 'react';\n\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\n\nexport function TeamSwitcher({\n  teams,\n}: {\n  teams: {\n    name: string;\n    logo: React.ElementType;\n    plan: string;\n  }[];\n}) {\n  const { isMobile } = useSidebar();\n  const [activeTeam, setActiveTeam] = React.useState(teams[0]);\n\n  if (!activeTeam) {\n    return null;\n  }\n\n  return (\n    <SidebarMenu>\n      <SidebarMenuItem>\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <SidebarMenuButton\n                className=\"data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground\"\n                size=\"lg\"\n              />\n            }\n          >\n            <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-accent text-sidebar-accent-foreground\">\n              <activeTeam.logo className=\"size-4\" />\n            </div>\n            <div className=\"grid flex-1 text-left text-sm leading-tight\">\n              <span className=\"truncate font-medium\">{activeTeam.name}</span>\n              <span className=\"truncate text-xs\">{activeTeam.plan}</span>\n            </div>\n            <ChevronsUpDown className=\"ml-auto\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"start\"\n            className=\"w-(--anchor-width) min-w-56 rounded-lg\"\n            side={isMobile ? 'bottom' : 'right'}\n            sideOffset={4}\n          >\n            <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n              Teams\n            </DropdownMenuLabel>\n            {teams.map((team, index) => (\n              <DropdownMenuItem\n                className=\"gap-2 p-2\"\n                key={team.name}\n                onClick={() => setActiveTeam(team)}\n              >\n                <div className=\"flex size-6 items-center justify-center rounded-md border\">\n                  <team.logo className=\"size-3.5 shrink-0\" />\n                </div>\n                {team.name}\n                <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>\n              </DropdownMenuItem>\n            ))}\n            <DropdownMenuSeparator />\n            <DropdownMenuItem className=\"gap-2 p-2\">\n              <div className=\"flex size-6 items-center justify-center rounded-md border bg-transparent\">\n                <Plus className=\"size-4\" />\n              </div>\n              <div className=\"font-medium text-muted-foreground\">Add team</div>\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </SidebarMenuItem>\n    </SidebarMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-05/app-sidebar.tsx",
          "type": "registry:component",
          "target": "components/sidebar-05/app-sidebar.tsx",
          "content": "'use client';\n\nimport {\n  IconActivityHeartbeat,\n  IconArchive,\n  IconBackground,\n  IconBellRinging,\n  IconBrandGoogle,\n  IconBrandMeta,\n  IconBrandNpm,\n  IconBrandOpenai,\n  IconBug,\n  IconChartBar,\n  IconChevronRight,\n  IconCloud,\n  IconDatabase,\n  IconFileText,\n  IconFolder,\n  IconFolders,\n  IconGitCommit,\n  IconGitMerge,\n  IconGitPullRequest,\n  IconHome,\n  IconKey,\n  IconLockExclamation,\n  IconLockPassword,\n  IconLogout,\n  IconNorthStar,\n  IconPackageExport,\n  IconPackages,\n  IconPasswordFingerprint,\n  IconPlayerPlay,\n  IconScanEye,\n  IconSettings,\n  IconShieldLock,\n  IconStar,\n  IconTarget,\n  IconTerminal2,\n  IconUser,\n  IconUserPlus,\n  IconWebhook,\n  IconX,\n} from '@tabler/icons-react';\nimport type React from 'react';\nimport { useState } from 'react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupContent,\n  SidebarHeader,\n  SidebarMenu,\n  SidebarMenuBadge,\n  SidebarMenuButton,\n  SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport { TeamSwitcher } from '@/components/sidebar-05/team-switcher';\n\nconst data = {\n  teams: [\n    {\n      name: 'OpenAI',\n      logo: IconBrandOpenai,\n      plan: 'Enterprise',\n    },\n    {\n      name: 'Anthropic',\n      logo: IconNorthStar,\n      plan: 'Pro',\n    },\n    {\n      name: 'Google',\n      logo: IconBrandGoogle,\n      plan: 'Free',\n    },\n    {\n      name: 'Meta',\n      logo: IconBrandMeta,\n      plan: 'Free',\n    },\n  ],\n};\n\ninterface SidebarItem {\n  id: string;\n  label: string;\n  icon: React.ComponentType<{ className?: string }>;\n  badge?: string;\n  route?: string;\n  subItems?: {\n    id: string;\n    label: string;\n    icon: React.ComponentType<{ className?: string }>;\n    description?: string;\n    route?: string;\n  }[];\n}\n\nconst sidebarItems: SidebarItem[] = [\n  {\n    id: 'overview',\n    label: 'Overview',\n    icon: IconHome,\n    subItems: [\n      {\n        id: 'dashboard',\n        label: 'Dashboard',\n        icon: IconChartBar,\n        description: 'Project overview and activity',\n        route: '/overview/dashboard',\n      },\n      {\n        id: 'activity',\n        label: 'Activity',\n        icon: IconActivityHeartbeat,\n        description: 'Recent commits and changes',\n        route: '/overview/activity',\n      },\n      {\n        id: 'insights',\n        label: 'Insights',\n        icon: IconTarget,\n        description: 'Code analytics and metrics',\n        route: '/overview/insights',\n      },\n    ],\n  },\n  {\n    id: 'repositories',\n    label: 'Repositories',\n    icon: IconFolders,\n    badge: '12',\n    subItems: [\n      {\n        id: 'all-repos',\n        label: 'All Repositories',\n        icon: IconFolder,\n        description: 'Browse all your repositories',\n        route: '/repositories',\n      },\n      {\n        id: 'starred',\n        label: 'Starred',\n        icon: IconStar,\n        description: 'Your starred repositories',\n        route: '/repositories/starred',\n      },\n      {\n        id: 'archived',\n        label: 'Archived',\n        icon: IconArchive,\n        description: 'Archived repositories',\n        route: '/repositories/archived',\n      },\n    ],\n  },\n  {\n    id: 'pull-requests',\n    label: 'Pull Requests',\n    icon: IconGitPullRequest,\n    badge: '3',\n    subItems: [\n      {\n        id: 'open-prs',\n        label: 'Open',\n        icon: IconGitPullRequest,\n        description: 'Open pull requests',\n        route: '/pull-requests/open',\n      },\n      {\n        id: 'review-requests',\n        label: 'Review Requests',\n        icon: IconScanEye,\n        description: 'PRs awaiting your review',\n        route: '/pull-requests/review',\n      },\n      {\n        id: 'merged',\n        label: 'Merged',\n        icon: IconGitMerge,\n        description: 'Recently merged PRs',\n        route: '/pull-requests/merged',\n      },\n    ],\n  },\n  {\n    id: 'issues',\n    label: 'Issues',\n    icon: IconBug,\n    badge: '7',\n    subItems: [\n      {\n        id: 'open-issues',\n        label: 'Open Issues',\n        icon: IconBug,\n        description: 'Active issues and bugs',\n        route: '/issues/open',\n      },\n      {\n        id: 'assigned',\n        label: 'Assigned to Me',\n        icon: IconUserPlus,\n        description: 'Issues assigned to you',\n        route: '/issues/assigned',\n      },\n      {\n        id: 'created',\n        label: 'Created by Me',\n        icon: IconGitCommit,\n        description: \"Issues you've created\",\n        route: '/issues/created',\n      },\n    ],\n  },\n  {\n    id: 'actions',\n    label: 'Actions',\n    icon: IconBackground,\n    subItems: [\n      {\n        id: 'workflows',\n        label: 'Workflows',\n        icon: IconPlayerPlay,\n        description: 'CI/CD workflows and pipelines',\n        route: '/actions/workflows',\n      },\n      {\n        id: 'runners',\n        label: 'Runners',\n        icon: IconTerminal2,\n        description: 'Self-hosted runners',\n        route: '/actions/runners',\n      },\n      {\n        id: 'deployments',\n        label: 'Deployments',\n        icon: IconCloud,\n        description: 'Deployment history',\n        route: '/actions/deployments',\n      },\n    ],\n  },\n  {\n    id: 'packages',\n    label: 'Packages',\n    icon: IconPackages,\n    subItems: [\n      {\n        id: 'published',\n        label: 'Published',\n        icon: IconPackageExport,\n        description: 'Your published packages',\n        route: '/packages/published',\n      },\n      {\n        id: 'container-registry',\n        label: 'Container Registry',\n        icon: IconDatabase,\n        description: 'Docker images and containers',\n        route: '/packages/containers',\n      },\n      {\n        id: 'npm-packages',\n        label: 'npm Packages',\n        icon: IconBrandNpm,\n        description: 'Node.js packages',\n        route: '/packages/npm',\n      },\n    ],\n  },\n  {\n    id: 'security',\n    label: 'Security',\n    icon: IconLockPassword,\n    badge: '2',\n    subItems: [\n      {\n        id: 'alerts',\n        label: 'Security Alerts',\n        icon: IconLockExclamation,\n        description: 'Vulnerability alerts',\n        route: '/security/alerts',\n      },\n      {\n        id: 'advisories',\n        label: 'Advisories',\n        icon: IconShieldLock,\n        description: 'Security advisories',\n        route: '/security/advisories',\n      },\n      {\n        id: 'secrets',\n        label: 'Secrets',\n        icon: IconPasswordFingerprint,\n        description: 'Repository secrets',\n        route: '/security/secrets',\n      },\n    ],\n  },\n  {\n    id: 'settings',\n    label: 'Settings',\n    icon: IconSettings,\n    subItems: [\n      {\n        id: 'profile',\n        label: 'Profile',\n        icon: IconUser,\n        description: 'Your profile settings',\n        route: '/settings/profile',\n      },\n      {\n        id: 'notifications',\n        label: 'Notifications',\n        icon: IconBellRinging,\n        description: 'Notification preferences',\n        route: '/settings/notifications',\n      },\n      {\n        id: 'webhooks',\n        label: 'Webhooks',\n        icon: IconWebhook,\n        description: 'Webhook configurations',\n        route: '/settings/webhooks',\n      },\n      {\n        id: 'api-keys',\n        label: 'API Keys',\n        icon: IconKey,\n        description: 'Personal access tokens',\n        route: '/settings/api-keys',\n      },\n    ],\n  },\n  {\n    id: 'docs',\n    label: 'Documentation',\n    icon: IconFileText,\n    route: '/docs',\n  },\n];\n\nexport function AppSidebar() {\n  const [activeItem, setActiveItem] = useState<string | null>('overview');\n  const [selectedSubItem, setSelectedSubItem] = useState<string | null>(null);\n\n  const activeItemData = sidebarItems.find((item) => item.id === activeItem);\n\n  const handleItemClick = (item: SidebarItem) => {\n    if (item.subItems) {\n      const isActive = activeItem === item.id;\n      setActiveItem(isActive ? null : item.id);\n      if (isActive) {\n        setSelectedSubItem(null);\n      }\n    } else {\n      if (activeItem) {\n        setActiveItem(null);\n        setSelectedSubItem(null);\n      }\n      console.log(`[v0] Navigating to: ${item.route}`);\n    }\n  };\n\n  const handleSubItemClick = (subItem: { id: string; route?: string }) => {\n    setSelectedSubItem(selectedSubItem === subItem.id ? null : subItem.id);\n    if (subItem.route) {\n      console.log(`[v0] Navigating to: ${subItem.route}`);\n    }\n  };\n\n  return (\n    <div className=\"flex h-dvh bg-background\">\n      <Sidebar\n        className=\"w-64 border-r\"\n        collapsible=\"none\"\n        side=\"left\"\n        variant=\"sidebar\"\n      >\n        <SidebarHeader>\n          <TeamSwitcher teams={data.teams} />\n        </SidebarHeader>\n\n        <SidebarContent>\n          <SidebarGroup>\n            <SidebarGroupContent>\n              <SidebarMenu>\n                {sidebarItems.map((item) => {\n                  const Icon = item.icon;\n                  const isActive = activeItem === item.id;\n                  const chevronIndicator = (\n                    <IconChevronRight\n                      className={cn(\n                        'h-4 w-4 shrink-0 transition-transform',\n                        isActive && 'rotate-90'\n                      )}\n                    />\n                  );\n\n                  return (\n                    <SidebarMenuItem key={item.id}>\n                      <SidebarMenuButton\n                        className=\"h-10 w-full px-3\"\n                        isActive={isActive}\n                        onClick={() => handleItemClick(item)}\n                      >\n                        <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n                          <Icon className=\"h-4 w-4 shrink-0\" />\n                          <span className=\"truncate\">{item.label}</span>\n                        </div>\n                        <div className=\"ml-auto flex min-w-fit shrink-0 items-center gap-1\">\n                          {(item.badge || item.subItems) &&\n                            (item.badge ? (\n                              <SidebarMenuBadge\n                                className={cn(\n                                  'min-w-fit',\n                                  item.subItems && 'gap-x-3'\n                                )}\n                              >\n                                {item.badge}\n                                {item.subItems && chevronIndicator}\n                              </SidebarMenuBadge>\n                            ) : (\n                              chevronIndicator\n                            ))}\n                        </div>\n                      </SidebarMenuButton>\n                    </SidebarMenuItem>\n                  );\n                })}\n              </SidebarMenu>\n            </SidebarGroupContent>\n          </SidebarGroup>\n        </SidebarContent>\n\n        <SidebarFooter>\n          <SidebarMenu>\n            <SidebarMenuItem>\n              <SidebarMenuButton className=\"h-12 w-full px-3\">\n                <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n                  <Avatar className=\"h-8 w-8 rounded-full\">\n                    <AvatarImage alt=\"ephraim\" src=\"/avatar-01.png\" />\n                    <AvatarFallback className=\"rounded-full\">E</AvatarFallback>\n                  </Avatar>\n                  <div className=\"min-w-0 flex-1 text-left\">\n                    <div className=\"truncate font-medium text-sm\">ephraim</div>\n                    <div className=\"truncate text-muted-foreground text-xs\">\n                      ephraim@blocks.so\n                    </div>\n                  </div>\n                </div>\n                <IconLogout className=\"h-4 w-4 shrink-0\" />\n              </SidebarMenuButton>\n            </SidebarMenuItem>\n          </SidebarMenu>\n        </SidebarFooter>\n      </Sidebar>\n\n      {activeItem && activeItemData?.subItems && (\n        <Sidebar\n          className=\"slide-in-from-left-5 w-72 animate-in border-r duration-200\"\n          collapsible=\"none\"\n          side=\"left\"\n          variant=\"sidebar\"\n        >\n          <SidebarHeader className=\"flex flex-row items-center justify-between border-b px-4\">\n            <h3 className=\"font-medium\">{activeItemData.label}</h3>\n            <button\n              className=\"flex h-6 w-6 items-center justify-center rounded-md p-0 hover:bg-sidebar-accent\"\n              onClick={() => setActiveItem(null)}\n            >\n              <IconX className=\"h-4 w-4\" />\n            </button>\n          </SidebarHeader>\n\n          <SidebarContent>\n            <SidebarGroup>\n              <SidebarGroupContent>\n                <SidebarMenu>\n                  {activeItemData.subItems.map((subItem) => {\n                    const SubIcon = subItem.icon;\n                    const isSelected = selectedSubItem === subItem.id;\n\n                    return (\n                      <SidebarMenuItem key={subItem.id}>\n                        <SidebarMenuButton\n                          className=\"h-auto w-full justify-start gap-3 px-3 py-2\"\n                          isActive={isSelected}\n                          onClick={() => handleSubItemClick(subItem)}\n                        >\n                          <SubIcon className=\"mt-0.5 h-5 w-5 shrink-0 self-start\" />\n\n                          <div className=\"min-w-0 flex-1 text-left\">\n                            <div className=\"font-medium\">{subItem.label}</div>\n                            {subItem.description && (\n                              <div className=\"mt-0.5 text-muted-foreground text-xs\">\n                                {subItem.description}\n                              </div>\n                            )}\n                          </div>\n                        </SidebarMenuButton>\n                      </SidebarMenuItem>\n                    );\n                  })}\n                </SidebarMenu>\n              </SidebarGroupContent>\n            </SidebarGroup>\n          </SidebarContent>\n        </Sidebar>\n      )}\n    </div>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-05/app/page.tsx",
          "type": "registry:page",
          "target": "app/page.tsx",
          "content": "'use client';\n\nimport { SidebarProvider } from '@/components/ui/sidebar';\nimport { AppSidebar } from '@/components/sidebar-05/app-sidebar';\n\nexport default function Page() {\n  return (\n    <SidebarProvider>\n      <div className=\"flex h-dvh w-full\">\n        <AppSidebar />\n      </div>\n    </SidebarProvider>\n  );\n}\n"
        }
      ],
      "categories": [
        "sidebar"
      ]
    },
    {
      "name": "sidebar-06",
      "type": "registry:block",
      "title": "Sidebar Replacement",
      "description": "A sidebar replacement block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "dropdown-menu",
        "sidebar"
      ],
      "dependencies": [
        "@tabler/icons-react",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/sidebar/sidebar-06/team-switcher.tsx",
          "type": "registry:component",
          "target": "components/sidebar-06/team-switcher.tsx",
          "content": "'use client';\n\nimport { ChevronsUpDown, Plus } from 'lucide-react';\nimport * as React from 'react';\n\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  useSidebar,\n} from '@/components/ui/sidebar';\n\nexport function TeamSwitcher({\n  teams,\n}: {\n  teams: {\n    name: string;\n    logo: React.ElementType;\n    plan: string;\n  }[];\n}) {\n  const { isMobile } = useSidebar();\n  const [activeTeam, setActiveTeam] = React.useState(teams[0]);\n\n  if (!activeTeam) {\n    return null;\n  }\n\n  return (\n    <SidebarMenu>\n      <SidebarMenuItem>\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <SidebarMenuButton\n                className=\"data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground\"\n                size=\"lg\"\n              />\n            }\n          >\n            <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-accent text-sidebar-accent-foreground\">\n              <activeTeam.logo className=\"size-4\" />\n            </div>\n            <div className=\"grid flex-1 text-left text-sm leading-tight\">\n              <span className=\"truncate font-medium\">{activeTeam.name}</span>\n              <span className=\"truncate text-xs\">{activeTeam.plan}</span>\n            </div>\n            <ChevronsUpDown className=\"ml-auto\" />\n          </DropdownMenuTrigger>\n          <DropdownMenuContent\n            align=\"start\"\n            className=\"w-(--anchor-width) min-w-56 rounded-lg\"\n            side={isMobile ? 'bottom' : 'right'}\n            sideOffset={4}\n          >\n            <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n              Teams\n            </DropdownMenuLabel>\n            {teams.map((team, index) => (\n              <DropdownMenuItem\n                className=\"gap-2 p-2\"\n                key={team.name}\n                onClick={() => setActiveTeam(team)}\n              >\n                <div className=\"flex size-6 items-center justify-center rounded-md border\">\n                  <team.logo className=\"size-3.5 shrink-0\" />\n                </div>\n                {team.name}\n                <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>\n              </DropdownMenuItem>\n            ))}\n            <DropdownMenuSeparator />\n            <DropdownMenuItem className=\"gap-2 p-2\">\n              <div className=\"flex size-6 items-center justify-center rounded-md border bg-transparent\">\n                <Plus className=\"size-4\" />\n              </div>\n              <div className=\"font-medium text-muted-foreground\">Add team</div>\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </SidebarMenuItem>\n    </SidebarMenu>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-06/app-sidebar.tsx",
          "type": "registry:component",
          "target": "components/sidebar-06/app-sidebar.tsx",
          "content": "'use client';\n\nimport {\n  IconActivityHeartbeat,\n  IconArchive,\n  IconArrowLeft,\n  IconBackground,\n  IconBellRinging,\n  IconBrandGoogle,\n  IconBrandMeta,\n  IconBrandNpm,\n  IconBrandOpenai,\n  IconBug,\n  IconChartBar,\n  IconChevronRight,\n  IconCloud,\n  IconDatabase,\n  IconFileText,\n  IconFolder,\n  IconFolders,\n  IconGitCommit,\n  IconGitMerge,\n  IconGitPullRequest,\n  IconHome,\n  IconKey,\n  IconLockExclamation,\n  IconLockPassword,\n  IconLogout,\n  IconNorthStar,\n  IconPackageExport,\n  IconPackages,\n  IconPasswordFingerprint,\n  IconPlayerPlay,\n  IconScanEye,\n  IconSettings,\n  IconShieldLock,\n  IconStar,\n  IconTarget,\n  IconTerminal2,\n  IconUser,\n  IconUserPlus,\n  IconWebhook,\n} from '@tabler/icons-react';\nimport type React from 'react';\nimport { useState } from 'react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupContent,\n  SidebarHeader,\n  SidebarMenu,\n  SidebarMenuBadge,\n  SidebarMenuButton,\n  SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport { cn } from '@/lib/utils';\nimport { TeamSwitcher } from '@/components/sidebar-06/team-switcher';\n\nconst data = {\n  teams: [\n    {\n      name: 'OpenAI',\n      logo: IconBrandOpenai,\n      plan: 'Enterprise',\n    },\n    {\n      name: 'Anthropic',\n      logo: IconNorthStar,\n      plan: 'Pro',\n    },\n    {\n      name: 'Google',\n      logo: IconBrandGoogle,\n      plan: 'Free',\n    },\n    {\n      name: 'Meta',\n      logo: IconBrandMeta,\n      plan: 'Free',\n    },\n  ],\n};\n\ninterface SidebarItem {\n  id: string;\n  label: string;\n  icon: React.ComponentType<{ className?: string }>;\n  badge?: string;\n  route?: string;\n  subItems?: {\n    id: string;\n    label: string;\n    icon: React.ComponentType<{ className?: string }>;\n    route?: string;\n  }[];\n}\n\nconst sidebarItems: SidebarItem[] = [\n  {\n    id: 'overview',\n    label: 'Overview',\n    icon: IconHome,\n    subItems: [\n      {\n        id: 'dashboard',\n        label: 'Dashboard',\n        icon: IconChartBar,\n        route: '/overview/dashboard',\n      },\n      {\n        id: 'activity',\n        label: 'Activity',\n        icon: IconActivityHeartbeat,\n        route: '/overview/activity',\n      },\n      {\n        id: 'insights',\n        label: 'Insights',\n        icon: IconTarget,\n        route: '/overview/insights',\n      },\n    ],\n  },\n  {\n    id: 'repositories',\n    label: 'Repositories',\n    icon: IconFolders,\n    badge: '12',\n    subItems: [\n      {\n        id: 'all-repos',\n        label: 'All Repositories',\n        icon: IconFolder,\n        route: '/repositories',\n      },\n      {\n        id: 'starred',\n        label: 'Starred',\n        icon: IconStar,\n        route: '/repositories/starred',\n      },\n      {\n        id: 'archived',\n        label: 'Archived',\n        icon: IconArchive,\n        route: '/repositories/archived',\n      },\n    ],\n  },\n  {\n    id: 'pull-requests',\n    label: 'Pull Requests',\n    icon: IconGitPullRequest,\n    badge: '3',\n    subItems: [\n      {\n        id: 'open-prs',\n        label: 'Open',\n        icon: IconGitPullRequest,\n        route: '/pull-requests/open',\n      },\n      {\n        id: 'review-requests',\n        label: 'Review Requests',\n        icon: IconScanEye,\n        route: '/pull-requests/review',\n      },\n      {\n        id: 'merged',\n        label: 'Merged',\n        icon: IconGitMerge,\n        route: '/pull-requests/merged',\n      },\n    ],\n  },\n  {\n    id: 'issues',\n    label: 'Issues',\n    icon: IconBug,\n    badge: '7',\n    subItems: [\n      {\n        id: 'open-issues',\n        label: 'Open Issues',\n        icon: IconBug,\n        route: '/issues/open',\n      },\n      {\n        id: 'assigned',\n        label: 'Assigned to Me',\n        icon: IconUserPlus,\n        route: '/issues/assigned',\n      },\n      {\n        id: 'created',\n        label: 'Created by Me',\n        icon: IconGitCommit,\n        route: '/issues/created',\n      },\n    ],\n  },\n  {\n    id: 'actions',\n    label: 'Actions',\n    icon: IconBackground,\n    subItems: [\n      {\n        id: 'workflows',\n        label: 'Workflows',\n        icon: IconPlayerPlay,\n        route: '/actions/workflows',\n      },\n      {\n        id: 'runners',\n        label: 'Runners',\n        icon: IconTerminal2,\n        route: '/actions/runners',\n      },\n      {\n        id: 'deployments',\n        label: 'Deployments',\n        icon: IconCloud,\n        route: '/actions/deployments',\n      },\n    ],\n  },\n  {\n    id: 'packages',\n    label: 'Packages',\n    icon: IconPackages,\n    subItems: [\n      {\n        id: 'published',\n        label: 'Published',\n        icon: IconPackageExport,\n        route: '/packages/published',\n      },\n      {\n        id: 'container-registry',\n        label: 'Container Registry',\n        icon: IconDatabase,\n        route: '/packages/containers',\n      },\n      {\n        id: 'npm-packages',\n        label: 'npm Packages',\n        icon: IconBrandNpm,\n        route: '/packages/npm',\n      },\n    ],\n  },\n  {\n    id: 'security',\n    label: 'Security',\n    icon: IconLockPassword,\n    badge: '2',\n    subItems: [\n      {\n        id: 'alerts',\n        label: 'Security Alerts',\n        icon: IconLockExclamation,\n        route: '/security/alerts',\n      },\n      {\n        id: 'advisories',\n        label: 'Advisories',\n        icon: IconShieldLock,\n        route: '/security/advisories',\n      },\n      {\n        id: 'secrets',\n        label: 'Secrets',\n        icon: IconPasswordFingerprint,\n        route: '/security/secrets',\n      },\n    ],\n  },\n  {\n    id: 'settings',\n    label: 'Settings',\n    icon: IconSettings,\n    subItems: [\n      {\n        id: 'profile',\n        label: 'Profile',\n        icon: IconUser,\n        route: '/settings/profile',\n      },\n      {\n        id: 'notifications',\n        label: 'Notifications',\n        icon: IconBellRinging,\n        route: '/settings/notifications',\n      },\n      {\n        id: 'webhooks',\n        label: 'Webhooks',\n        icon: IconWebhook,\n        route: '/settings/webhooks',\n      },\n      {\n        id: 'api-keys',\n        label: 'API Keys',\n        icon: IconKey,\n        route: '/settings/api-keys',\n      },\n    ],\n  },\n  {\n    id: 'docs',\n    label: 'Documentation',\n    icon: IconFileText,\n    route: '/docs',\n  },\n];\n\nexport function AppSidebar() {\n  const [activeItem, setActiveItem] = useState<string | null>(null);\n  const [selectedSubItem, setSelectedSubItem] = useState<string | null>(null);\n\n  const activeItemData = sidebarItems.find((item) => item.id === activeItem);\n\n  const handleItemClick = (item: SidebarItem) => {\n    if (item.subItems) {\n      setActiveItem(item.id);\n      setSelectedSubItem(null);\n    } else {\n      console.log(`[v0] Navigating to: ${item.route}`);\n    }\n  };\n\n  const handleSubItemClick = (subItem: { id: string; route?: string }) => {\n    setSelectedSubItem(selectedSubItem === subItem.id ? null : subItem.id);\n    if (subItem.route) {\n      console.log(`[v0] Navigating to: ${subItem.route}`);\n    }\n  };\n\n  const handleBackToMain = () => {\n    setActiveItem(null);\n    setSelectedSubItem(null);\n  };\n\n  return (\n    <div className=\"flex h-dvh bg-background\">\n      <Sidebar\n        className=\"w-64 border-r\"\n        collapsible=\"none\"\n        side=\"left\"\n        variant=\"sidebar\"\n      >\n        {activeItem ? (\n          activeItemData?.subItems && (\n            <>\n              <SidebarHeader className=\"flex flex-row items-center justify-between border-b px-4\">\n                <button\n                  className=\"flex h-8 w-8 items-center justify-center rounded-md p-0 hover:bg-sidebar-accent\"\n                  onClick={handleBackToMain}\n                >\n                  <IconArrowLeft className=\"h-4 w-4\" />\n                </button>\n                <h3 className=\"flex-1 text-center font-medium\">\n                  {activeItemData.label}\n                </h3>\n                <div className=\"w-8\" />\n              </SidebarHeader>\n\n              <SidebarContent>\n                <SidebarGroup>\n                  <SidebarGroupContent>\n                    <SidebarMenu>\n                      {activeItemData.subItems.map((subItem) => {\n                        const SubIcon = subItem.icon;\n                        const isSelected = selectedSubItem === subItem.id;\n\n                        return (\n                          <SidebarMenuItem key={subItem.id}>\n                            <SidebarMenuButton\n                              className=\"h-10 w-full px-3\"\n                              isActive={isSelected}\n                              onClick={() => handleSubItemClick(subItem)}\n                            >\n                              <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n                                <SubIcon className=\"h-4 w-4 shrink-0\" />\n                                <span className=\"truncate\">\n                                  {subItem.label}\n                                </span>\n                              </div>\n                            </SidebarMenuButton>\n                          </SidebarMenuItem>\n                        );\n                      })}\n                    </SidebarMenu>\n                  </SidebarGroupContent>\n                </SidebarGroup>\n              </SidebarContent>\n            </>\n          )\n        ) : (\n          <>\n            <SidebarHeader>\n              <TeamSwitcher teams={data.teams} />\n            </SidebarHeader>\n\n            <SidebarContent>\n              <SidebarGroup>\n                <SidebarGroupContent>\n                  <SidebarMenu>\n                    {sidebarItems.map((item) => {\n                      const Icon = item.icon;\n                      const chevronIndicator = (\n                        <IconChevronRight className=\"h-4 w-4 shrink-0 transition-transform\" />\n                      );\n\n                      return (\n                        <SidebarMenuItem key={item.id}>\n                          <SidebarMenuButton\n                            className=\"h-10 w-full px-3\"\n                            onClick={() => handleItemClick(item)}\n                          >\n                            <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n                              <Icon className=\"h-4 w-4 shrink-0\" />\n                              <span className=\"truncate\">{item.label}</span>\n                            </div>\n                            <div className=\"ml-auto flex min-w-fit shrink-0 items-center gap-1\">\n                              {(item.badge || item.subItems) &&\n                                (item.badge ? (\n                                  <SidebarMenuBadge\n                                    className={cn(\n                                      'min-w-fit',\n                                      item.subItems && 'gap-x-3'\n                                    )}\n                                  >\n                                    {item.badge}\n                                    {item.subItems && chevronIndicator}\n                                  </SidebarMenuBadge>\n                                ) : (\n                                  chevronIndicator\n                                ))}\n                            </div>\n                          </SidebarMenuButton>\n                        </SidebarMenuItem>\n                      );\n                    })}\n                  </SidebarMenu>\n                </SidebarGroupContent>\n              </SidebarGroup>\n            </SidebarContent>\n\n            <SidebarFooter>\n              <SidebarMenu>\n                <SidebarMenuItem>\n                  <SidebarMenuButton className=\"h-12 w-full px-3\">\n                    <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n                      <Avatar className=\"h-8 w-8 rounded-full\">\n                        <AvatarImage alt=\"ephraim\" src=\"/avatar-01.png\" />\n                        <AvatarFallback className=\"rounded-full\">\n                          E\n                        </AvatarFallback>\n                      </Avatar>\n                      <div className=\"min-w-0 flex-1 text-left\">\n                        <div className=\"truncate font-medium text-sm\">\n                          ephraim\n                        </div>\n                        <div className=\"truncate text-muted-foreground text-xs\">\n                          ephraim@blocks.so\n                        </div>\n                      </div>\n                    </div>\n                    <IconLogout className=\"h-4 w-4 shrink-0\" />\n                  </SidebarMenuButton>\n                </SidebarMenuItem>\n              </SidebarMenu>\n            </SidebarFooter>\n          </>\n        )}\n      </Sidebar>\n    </div>\n  );\n}\n"
        },
        {
          "path": "content/components/sidebar/sidebar-06/app/page.tsx",
          "type": "registry:page",
          "target": "app/page.tsx",
          "content": "'use client';\n\nimport { SidebarProvider } from '@/components/ui/sidebar';\nimport { AppSidebar } from '@/components/sidebar-06/app-sidebar';\n\nexport default function Page() {\n  return (\n    <SidebarProvider>\n      <div className=\"flex h-dvh w-full\">\n        <AppSidebar />\n      </div>\n    </SidebarProvider>\n  );\n}\n"
        }
      ],
      "categories": [
        "sidebar"
      ]
    },
    {
      "name": "stats-01",
      "type": "registry:block",
      "title": "Stats with Trending",
      "description": "A stats with trending block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/stats/stats-01.tsx",
          "type": "registry:component",
          "target": "components/stats-01.tsx",
          "content": "import { Card, CardContent } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\nconst data = [\n  {\n    name: 'Profit',\n    value: '$287,654.00',\n    change: '+8.32%',\n    changeType: 'positive',\n  },\n  {\n    name: 'Late payments',\n    value: '$9,435.00',\n    change: '-12.64%',\n    changeType: 'negative',\n  },\n  {\n    name: 'Pending orders',\n    value: '$173,229.00',\n    change: '+2.87%',\n    changeType: 'positive',\n  },\n  {\n    name: 'Operating costs',\n    value: '$52,891.00',\n    change: '-5.73%',\n    changeType: 'negative',\n  },\n];\n\nexport default function Stats01() {\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <div className=\"mx-auto grid grid-cols-1 gap-px rounded-xl bg-border sm:grid-cols-2 lg:grid-cols-4\">\n        {data.map((stat, index) => (\n          <Card\n            className={cn(\n              'rounded-none border-0 py-0 shadow-none',\n              index === 0 && 'rounded-l-xl',\n              index === data.length - 1 && 'rounded-r-xl'\n            )}\n            key={stat.name}\n          >\n            <CardContent className=\"flex flex-wrap items-baseline justify-between gap-x-4 gap-y-2 p-4 sm:p-6\">\n              <div className=\"font-medium text-muted-foreground text-sm\">\n                {stat.name}\n              </div>\n              <div\n                className={cn(\n                  'font-medium text-xs tabular-nums',\n                  stat.changeType === 'positive'\n                    ? 'text-green-800 dark:text-green-400'\n                    : 'text-red-800 dark:text-red-400'\n                )}\n              >\n                {stat.change}\n              </div>\n              <div className=\"w-full flex-none font-medium text-3xl text-foreground tabular-nums tracking-tight\">\n                {stat.value}\n              </div>\n            </CardContent>\n          </Card>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-02",
      "type": "registry:block",
      "title": "Stats with Borders",
      "description": "A stats with borders block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "card"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-02.tsx",
          "type": "registry:component",
          "target": "components/stats-02.tsx",
          "content": "import { TrendingDown, TrendingUp } from 'lucide-react';\nimport { Badge } from '@/components/ui/badge';\nimport { Card, CardContent, CardTitle } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\nconst stats = [\n  {\n    metric: 'Active Users',\n    current: '128,456',\n    previous: '115,789',\n    difference: '10.9%',\n    trend: 'up',\n  },\n  {\n    metric: 'Conversion Rate',\n    current: '5.32%',\n    previous: '6.18%',\n    difference: '0.86%',\n    trend: 'down',\n  },\n  {\n    metric: 'Avg. Session Duration',\n    current: '3m 42s',\n    previous: '3m 15s',\n    difference: '13.8%',\n    trend: 'up',\n  },\n];\n\nexport default function Stats02() {\n  return (\n    <div className=\"flex items-center justify-center p-10\">\n      <div className=\"grid grid-cols-1 divide-y divide-border overflow-hidden rounded-lg bg-border md:grid-cols-3 md:divide-x md:divide-y-0\">\n        {stats.map((item) => (\n          <Card\n            className=\"rounded-none border-0 py-0 shadow-sm\"\n            key={item.metric}\n          >\n            <CardContent className=\"p-4 sm:p-6\">\n              <CardTitle className=\"font-normal text-base\">\n                {item.metric}\n              </CardTitle>\n              <div className=\"mt-1 flex items-baseline gap-2 md:block lg:flex\">\n                <div className=\"flex items-baseline font-semibold text-2xl text-primary tabular-nums\">\n                  {item.current}\n                  <span className=\"ml-2 font-medium text-muted-foreground text-sm tabular-nums\">\n                    from {item.previous}\n                  </span>\n                </div>\n\n                <Badge\n                  className={cn(\n                    'inline-flex h-auto items-center border-transparent px-1.5 py-0.5 ps-2.5 font-medium text-xs tabular-nums md:mt-2 lg:mt-0 [&>svg]:size-5!',\n                    item.trend === 'up'\n                      ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'\n                      : 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'\n                  )}\n                  variant=\"outline\"\n                >\n                  {item.trend === 'up' ? (\n                    <TrendingUp className=\"-ml-1 mr-0.5 h-5 w-5 shrink-0 self-center text-green-500\" />\n                  ) : (\n                    <TrendingDown className=\"-ml-1 mr-0.5 h-5 w-5 shrink-0 self-center text-red-500\" />\n                  )}\n\n                  <span className=\"sr-only\">\n                    {' '}\n                    {item.trend === 'up' ? 'Increased' : 'Decreased'} by{' '}\n                  </span>\n                  {item.difference}\n                </Badge>\n              </div>\n            </CardContent>\n          </Card>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-03",
      "type": "registry:block",
      "title": "Stats with Card Layout",
      "description": "A stats with card layout block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/stats/stats-03.tsx",
          "type": "registry:component",
          "target": "components/stats-03.tsx",
          "content": "'use client';\n\nimport { Card, CardContent } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\nconst data = [\n  {\n    name: 'Unique visitors',\n    stat: '10,450',\n    change: '-12.5%',\n    changeType: 'negative',\n  },\n  {\n    name: 'Bounce rate',\n    stat: '56.1%',\n    change: '+1.8%',\n    changeType: 'positive',\n  },\n  {\n    name: 'Visit duration',\n    stat: '5.2min',\n    change: '+19.7%',\n    changeType: 'positive',\n  },\n  {\n    name: 'Conversion rate',\n    stat: '3.2%',\n    change: '-2.4%',\n    changeType: 'negative',\n  },\n];\n\nexport default function Stats03() {\n  return (\n    <div className=\"flex w-full items-center justify-center p-10\">\n      <dl className=\"grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4\">\n        {data.map((item) => (\n          <Card className=\"p-6 py-4 shadow-2xs\" key={item.name}>\n            <CardContent className=\"p-0\">\n              <dt className=\"font-medium text-muted-foreground text-sm\">\n                {item.name}\n              </dt>\n              <dd className=\"mt-2 flex items-baseline space-x-2.5\">\n                <span className=\"font-semibold text-3xl text-foreground tabular-nums\">\n                  {item.stat}\n                </span>\n                <span\n                  className={cn(\n                    item.changeType === 'positive'\n                      ? 'text-green-800 dark:text-green-400'\n                      : 'text-red-800 dark:text-red-400',\n                    'font-medium text-sm'\n                  )}\n                >\n                  {item.change}\n                </span>\n              </dd>\n            </CardContent>\n          </Card>\n        ))}\n      </dl>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-04",
      "type": "registry:block",
      "title": "Stats with Badges",
      "description": "A stats with badges block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "card"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-04.tsx",
          "type": "registry:component",
          "target": "components/stats-04.tsx",
          "content": "'use client';\n\nimport { TrendingDown, TrendingUp } from 'lucide-react';\nimport { Badge } from '@/components/ui/badge';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\nconst data = [\n  {\n    name: 'Daily active users',\n    stat: '3,450',\n    change: '+12.1%',\n    changeType: 'positive',\n  },\n  {\n    name: 'Weekly sessions',\n    stat: '1,342',\n    change: '-9.8%',\n    changeType: 'negative',\n  },\n  {\n    name: 'Duration',\n    stat: '5.2min',\n    change: '+7.7%',\n    changeType: 'positive',\n  },\n];\n\nexport default function Stats04() {\n  return (\n    <div className=\"flex w-full items-center justify-center p-10\">\n      <dl className=\"grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3\">\n        {data.map((item) => (\n          <Card className=\"w-full p-6 py-4 shadow-2xs\" key={item.name}>\n            <CardContent className=\"p-0\">\n              <div className=\"flex items-center justify-between\">\n                <dt className=\"font-medium text-muted-foreground text-sm\">\n                  {item.name}\n                </dt>\n                <Badge\n                  className={cn(\n                    'tabular-nums',\n                    item.changeType === 'positive'\n                      ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'\n                      : 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'\n                  )}\n                >\n                  {item.changeType === 'positive' ? (\n                    <TrendingUp aria-hidden={true} data-icon=\"inline-start\" />\n                  ) : (\n                    <TrendingDown aria-hidden={true} data-icon=\"inline-start\" />\n                  )}\n                  <span className=\"sr-only\">\n                    {item.changeType === 'positive' ? 'Increased' : 'Decreased'}{' '}\n                    by{' '}\n                  </span>\n                  {item.change}\n                </Badge>\n              </div>\n              <dd className=\"mt-2 font-semibold text-3xl text-foreground tabular-nums\">\n                {item.stat}\n              </dd>\n            </CardContent>\n          </Card>\n        ))}\n      </dl>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-05",
      "type": "registry:block",
      "title": "Stats with Links",
      "description": "A stats with links block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/stats/stats-05.tsx",
          "type": "registry:component",
          "target": "components/stats-05.tsx",
          "content": "'use client';\n\nimport { Card, CardContent, CardFooter } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\nconst data = [\n  {\n    name: 'Monthly recurring revenue',\n    value: '$34.1K',\n    change: '+6.1%',\n    changeType: 'positive',\n    href: '#',\n  },\n  {\n    name: 'Users',\n    value: '500.1K',\n    change: '+19.2%',\n    changeType: 'positive',\n    href: '#',\n  },\n  {\n    name: 'User growth',\n    value: '11.3%',\n    change: '-1.2%',\n    changeType: 'negative',\n    href: '#',\n  },\n];\n\nexport default function Stats05() {\n  return (\n    <div className=\"flex w-full items-center justify-center p-10\">\n      <dl className=\"grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3\">\n        {data.map((item) => (\n          <Card className=\"gap-0 p-0 shadow-2xs\" key={item.name}>\n            <CardContent className=\"p-6\">\n              <dd className=\"flex items-start justify-between space-x-2\">\n                <span className=\"truncate text-muted-foreground text-sm\">\n                  {item.name}\n                </span>\n                <span\n                  className={cn(\n                    'font-medium text-sm',\n                    item.changeType === 'positive'\n                      ? 'text-emerald-700 dark:text-emerald-500'\n                      : 'text-red-700 dark:text-red-500'\n                  )}\n                >\n                  {item.change}\n                </span>\n              </dd>\n              <dd className=\"mt-1 font-semibold text-3xl text-foreground tabular-nums\">\n                {item.value}\n              </dd>\n            </CardContent>\n            <CardFooter className=\"flex justify-end border-border border-t p-0!\">\n              <a\n                className=\"px-6 py-3 font-medium text-primary text-sm hover:text-primary/90\"\n                href={item.href}\n              >\n                View more &#8594;\n              </a>\n            </CardFooter>\n          </Card>\n        ))}\n      </dl>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-06",
      "type": "registry:block",
      "title": "Stats with Status",
      "description": "A stats with status block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-06.tsx",
          "type": "registry:component",
          "target": "components/stats-06.tsx",
          "content": "'use client';\n\nimport { ArrowRight } from 'lucide-react';\nimport Link from 'next/link';\nimport { cn } from '@/lib/utils';\n\nconst statuses = {\n  within: { label: 'On track', color: 'bg-emerald-500' },\n  observe: { label: 'Needs attention', color: 'bg-amber-500' },\n  critical: { label: 'At risk', color: 'bg-red-500' },\n};\n\nconst data = [\n  {\n    name: 'Europe',\n    stat: '$10,023',\n    goalsAchieved: 3,\n    status: 'observe',\n    href: '#',\n  },\n  {\n    name: 'North America',\n    stat: '$14,092',\n    goalsAchieved: 5,\n    status: 'within',\n    href: '#',\n  },\n  {\n    name: 'Asia',\n    stat: '$113,232',\n    goalsAchieved: 1,\n    status: 'critical',\n    href: '#',\n  },\n] as {\n  name: string;\n  stat: string;\n  goalsAchieved: number;\n  status: keyof typeof statuses;\n  href: string;\n}[];\n\nconst goals = [1, 2, 3, 4, 5];\n\nexport default function Stats06() {\n  return (\n    <div className=\"@container flex w-full items-center justify-center p-10\">\n      <dl className=\"grid w-full @2xl:grid-cols-3 grid-cols-1 @2xl:divide-x divide-y @2xl:divide-y-0 divide-foreground/10 overflow-hidden rounded-xl bg-background shadow-[0_0_0_1px_oklch(0_0_0/0.06),0_1px_2px_-1px_oklch(0_0_0/0.06),0_2px_4px_0_oklch(0_0_0/0.04)] dark:shadow-[0_0_0_1px_oklch(1_0_0/0.08)]\">\n        {data.map((item) => {\n          const status = statuses[item.status];\n          return (\n            <div\n              className=\"group relative flex flex-col gap-6 p-6 hover:bg-muted/40\"\n              key={item.name}\n            >\n              <div className=\"flex items-center justify-between gap-3\">\n                <dt className=\"truncate font-medium text-foreground text-sm\">\n                  {item.name}\n                </dt>\n                <dd className=\"flex shrink-0 items-center gap-2 text-muted-foreground text-sm\">\n                  <span\n                    aria-hidden={true}\n                    className={cn('size-1.5 rounded-full', status.color)}\n                  />\n                  {status.label}\n                </dd>\n              </div>\n              <dd className=\"font-semibold text-3xl text-foreground tabular-nums tracking-tight\">\n                {item.stat}\n              </dd>\n              <dd className=\"flex flex-col gap-2\">\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">Goals achieved</span>\n                  <span className=\"font-medium text-foreground tabular-nums\">\n                    {item.goalsAchieved}/{goals.length}\n                  </span>\n                </div>\n                <div aria-hidden={true} className=\"flex gap-1\">\n                  {goals.map((goal) => (\n                    <span\n                      className={cn(\n                        'h-1.5 flex-1 rounded-full',\n                        goal <= item.goalsAchieved\n                          ? status.color\n                          : 'bg-foreground/10'\n                      )}\n                      key={goal}\n                    />\n                  ))}\n                </div>\n              </dd>\n              <dd className=\"text-sm\">\n                <Link\n                  className=\"flex items-center gap-1 font-medium text-foreground focus:outline-none\"\n                  href={item.href}\n                >\n                  <span aria-hidden={true} className=\"absolute inset-0\" />\n                  View region\n                  <ArrowRight\n                    aria-hidden={true}\n                    className=\"size-4 shrink-0 transition-transform group-hover:translate-x-0.5\"\n                  />\n                </Link>\n              </dd>\n            </div>\n          );\n        })}\n      </dl>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-07",
      "type": "registry:block",
      "title": "Stats with Circular Progress",
      "description": "A stats with circular progress block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card",
        "chart"
      ],
      "dependencies": [
        "lucide-react",
        "recharts"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-07.tsx",
          "type": "registry:component",
          "target": "components/stats-07.tsx",
          "content": "'use client';\n\nimport { ExternalLink } from 'lucide-react';\nimport Link from 'next/link';\nimport { PolarAngleAxis, RadialBar, RadialBarChart } from 'recharts';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { type ChartConfig, ChartContainer } from '@/components/ui/chart';\n\nconst data = [\n  {\n    name: 'Workspaces',\n    capacity: 20,\n    current: 1,\n    allowed: 5,\n    fill: 'var(--chart-1)',\n  },\n  {\n    name: 'Dashboards',\n    capacity: 10,\n    current: 2,\n    allowed: 20,\n    fill: 'var(--chart-2)',\n  },\n  {\n    name: 'Chart widgets',\n    capacity: 30,\n    current: 15,\n    allowed: 50,\n    fill: 'var(--chart-3)',\n  },\n  {\n    name: 'Storage',\n    capacity: 50,\n    current: 25,\n    allowed: 100,\n    fill: 'var(--chart-4)',\n  },\n];\n\nconst chartConfig = {\n  capacity: {\n    label: 'Capacity',\n    color: 'hsl(var(--primary))',\n  },\n} satisfies ChartConfig;\n\nexport default function Stats07() {\n  return (\n    <div className=\"flex w-full items-center justify-center p-10\">\n      <div className=\"w-full\">\n        <h2 className=\"text-balance font-medium text-foreground text-xl\">\n          Plan overview\n        </h2>\n        <p className=\"mt-1 text-pretty text-muted-foreground text-sm leading-6\">\n          You are currently on the{' '}\n          <span className=\"font-medium text-foreground\">starter plan</span>.{' '}\n          <Link\n            className=\"inline-flex items-center gap-1 text-primary hover:underline hover:underline-offset-4\"\n            href=\"#\"\n          >\n            View other plans\n            <ExternalLink aria-hidden={true} className=\"size-4\" />\n          </Link>\n        </p>\n        <dl className=\"mt-6 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4\">\n          {data.map((item) => (\n            <Card className=\"p-4 shadow-2xs\" key={item.name}>\n              <CardContent className=\"flex items-center space-x-4 p-0\">\n                <div className=\"relative flex items-center justify-center\">\n                  <ChartContainer\n                    className=\"h-[80px] w-[80px]\"\n                    config={chartConfig}\n                  >\n                    <RadialBarChart\n                      barSize={6}\n                      data={[item]}\n                      endAngle={-270}\n                      innerRadius={30}\n                      outerRadius={40}\n                      startAngle={90}\n                    >\n                      <PolarAngleAxis\n                        angleAxisId={0}\n                        axisLine={false}\n                        domain={[0, 100]}\n                        tick={false}\n                        type=\"number\"\n                      />\n                      <RadialBar\n                        angleAxisId={0}\n                        background\n                        cornerRadius={10}\n                        dataKey=\"capacity\"\n                        fill=\"var(--primary)\"\n                      />\n                    </RadialBarChart>\n                  </ChartContainer>\n                  <div className=\"absolute inset-0 flex items-center justify-center\">\n                    <span className=\"font-medium text-base text-foreground\">\n                      {item.capacity}%\n                    </span>\n                  </div>\n                </div>\n                <div>\n                  <dt className=\"font-medium text-foreground text-sm\">\n                    {item.name}\n                  </dt>\n                  <dd className=\"text-muted-foreground text-sm\">\n                    {item.current} of {item.allowed} used\n                  </dd>\n                </div>\n              </CardContent>\n            </Card>\n          ))}\n        </dl>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-08",
      "type": "registry:block",
      "title": "Stats with Circular Progress and Links",
      "description": "A stats with circular progress and links block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card",
        "chart"
      ],
      "dependencies": [
        "recharts"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-08.tsx",
          "type": "registry:component",
          "target": "components/stats-08.tsx",
          "content": "'use client';\n\nimport { PolarAngleAxis, RadialBar, RadialBarChart } from 'recharts';\nimport { Card, CardContent, CardFooter } from '@/components/ui/card';\nimport { type ChartConfig, ChartContainer } from '@/components/ui/chart';\n\nconst data = [\n  {\n    name: 'HR',\n    progress: 25,\n    budget: '$1,000',\n    current: '$250',\n    href: '#',\n    fill: 'var(--chart-1)',\n  },\n  {\n    name: 'Marketing',\n    progress: 55,\n    budget: '$1,000',\n    current: '$550',\n    href: '#',\n    fill: 'var(--chart-2)',\n  },\n  {\n    name: 'Finance',\n    progress: 85,\n    budget: '$1,000',\n    current: '$850',\n    href: '#',\n    fill: 'var(--chart-3)',\n  },\n  {\n    name: 'Engineering',\n    progress: 70,\n    budget: '$2,000',\n    current: '$1,400',\n    href: '#',\n    fill: 'var(--chart-4)',\n  },\n];\n\nconst chartConfig = {\n  progress: {\n    label: 'Progress',\n    color: 'var(--primary)',\n  },\n} satisfies ChartConfig;\n\nexport default function Stats08() {\n  return (\n    <div className=\"flex w-full items-center justify-center p-10\">\n      <dl className=\"grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4\">\n        {data.map((item) => (\n          <Card className=\"gap-0 p-0 shadow-2xs\" key={item.name}>\n            <CardContent className=\"p-4\">\n              <div className=\"flex items-center space-x-3\">\n                <div className=\"relative flex items-center justify-center\">\n                  <ChartContainer\n                    className=\"h-[80px] w-[80px]\"\n                    config={chartConfig}\n                  >\n                    <RadialBarChart\n                      barSize={6}\n                      data={[item]}\n                      endAngle={-270}\n                      innerRadius={30}\n                      outerRadius={40}\n                      startAngle={90}\n                    >\n                      <PolarAngleAxis\n                        angleAxisId={0}\n                        axisLine={false}\n                        domain={[0, 100]}\n                        tick={false}\n                        type=\"number\"\n                      />\n                      <RadialBar\n                        angleAxisId={0}\n                        background\n                        cornerRadius={10}\n                        dataKey=\"progress\"\n                        fill={item.fill}\n                      />\n                    </RadialBarChart>\n                  </ChartContainer>\n                  <div className=\"absolute inset-0 flex items-center justify-center\">\n                    <span className=\"font-medium text-base text-foreground\">\n                      {item.progress}%\n                    </span>\n                  </div>\n                </div>\n                <div>\n                  <dd className=\"font-medium text-base text-foreground\">\n                    {item.current} / {item.budget}\n                  </dd>\n                  <dt className=\"text-muted-foreground text-sm\">\n                    Budget {item.name}\n                  </dt>\n                </div>\n              </div>\n            </CardContent>\n            <CardFooter className=\"flex items-center justify-end border-border border-t p-0!\">\n              <a\n                className=\"px-6 py-3 font-medium text-primary text-sm hover:text-primary/90\"\n                href={item.href}\n              >\n                View more &#8594;\n              </a>\n            </CardFooter>\n          </Card>\n        ))}\n      </dl>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-09",
      "type": "registry:block",
      "title": "Stats with Progress",
      "description": "A stats with progress block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card",
        "progress"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/stats/stats-09.tsx",
          "type": "registry:component",
          "target": "components/stats-09.tsx",
          "content": "'use client';\n\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Progress } from '@/components/ui/progress';\n\nconst data = [\n  {\n    name: 'Requests',\n    stat: '996',\n    limit: '10,000',\n    percentage: 9.96,\n  },\n  {\n    name: 'Credits',\n    stat: '$672',\n    limit: '$1,000',\n    percentage: 67.2,\n  },\n  {\n    name: 'Storage',\n    stat: '1.85',\n    limit: '10GB',\n    percentage: 18.5,\n  },\n  {\n    name: 'API Calls',\n    stat: '4,328',\n    limit: '5,000',\n    percentage: 86.56,\n  },\n];\n\nexport default function Stats09() {\n  return (\n    <div className=\"flex w-full items-center justify-center p-10\">\n      <dl className=\"grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4\">\n        {data.map((item) => (\n          <Card className=\"py-4 shadow-2xs\" key={item.name}>\n            <CardContent className=\"\">\n              <dt className=\"text-muted-foreground text-sm\">{item.name}</dt>\n              <dd className=\"font-semibold text-2xl text-foreground tabular-nums\">\n                {item.stat}\n              </dd>\n              <Progress className=\"mt-6 h-2\" value={item.percentage} />\n              <dd className=\"mt-2 flex items-center justify-between text-sm\">\n                <span className=\"text-primary\">{item.percentage}&#37;</span>\n                <span className=\"text-muted-foreground\">\n                  {item.stat} of {item.limit}\n                </span>\n              </dd>\n            </CardContent>\n          </Card>\n        ))}\n      </dl>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-10",
      "type": "registry:block",
      "title": "Stats with Area Chart",
      "description": "A stats with area chart block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card",
        "chart"
      ],
      "dependencies": [
        "recharts"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-10.tsx",
          "type": "registry:component",
          "target": "components/stats-10.tsx",
          "content": "'use client';\n\nimport { Area, AreaChart, XAxis } from 'recharts';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { ChartContainer } from '@/components/ui/chart';\nimport { cn } from '@/lib/utils';\n\nconst data = [\n  {\n    date: 'Nov 24, 2023',\n    'Alpha Corp': 142.87,\n    'Beta Solutions': 65.32,\n    'Gamma Industries': 83.25,\n  },\n  {\n    date: 'Nov 25, 2023',\n    'Alpha Corp': 151.43,\n    'Beta Solutions': 59.78,\n    'Gamma Industries': 79.64,\n  },\n  {\n    date: 'Nov 26, 2023',\n    'Alpha Corp': 157.28,\n    'Beta Solutions': 64.21,\n    'Gamma Industries': 76.19,\n  },\n  {\n    date: 'Nov 27, 2023',\n    'Alpha Corp': 162.94,\n    'Beta Solutions': 57.46,\n    'Gamma Industries': 72.84,\n  },\n  {\n    date: 'Nov 28, 2023',\n    'Alpha Corp': 148.37,\n    'Beta Solutions': 49.82,\n    'Gamma Industries': 81.56,\n  },\n  {\n    date: 'Nov 29, 2023',\n    'Alpha Corp': 139.56,\n    'Beta Solutions': 55.63,\n    'Gamma Industries': 92.38,\n  },\n  {\n    date: 'Nov 30, 2023',\n    'Alpha Corp': 145.83,\n    'Beta Solutions': 61.27,\n    'Gamma Industries': 88.75,\n  },\n  {\n    date: 'Dec 01, 2023',\n    'Alpha Corp': 138.29,\n    'Beta Solutions': 68.94,\n    'Gamma Industries': 93.42,\n  },\n  {\n    date: 'Dec 02, 2023',\n    'Alpha Corp': 129.64,\n    'Beta Solutions': 74.56,\n    'Gamma Industries': 97.18,\n  },\n  {\n    date: 'Dec 03, 2023',\n    'Alpha Corp': 119.82,\n    'Beta Solutions': 71.38,\n    'Gamma Industries': 89.43,\n  },\n  {\n    date: 'Dec 04, 2023',\n    'Alpha Corp': 128.54,\n    'Beta Solutions': 63.95,\n    'Gamma Industries': 92.76,\n  },\n  {\n    date: 'Dec 05, 2023',\n    'Alpha Corp': 137.21,\n    'Beta Solutions': 58.47,\n    'Gamma Industries': 84.29,\n  },\n  {\n    date: 'Dec 06, 2023',\n    'Alpha Corp': 134.68,\n    'Beta Solutions': 69.12,\n    'Gamma Industries': 79.38,\n  },\n  {\n    date: 'Dec 07, 2023',\n    'Alpha Corp': 152.73,\n    'Beta Solutions': 73.89,\n    'Gamma Industries': 81.42,\n  },\n  {\n    date: 'Dec 08, 2023',\n    'Alpha Corp': 168.59,\n    'Beta Solutions': 78.54,\n    'Gamma Industries': 75.68,\n  },\n];\n\nconst summary = [\n  {\n    name: 'Alpha Corp',\n    tickerSymbol: 'ACP',\n    value: '$168.59',\n    change: '+15.86',\n    percentageChange: '+10.4%',\n    changeType: 'positive',\n  },\n  {\n    name: 'Beta Solutions',\n    tickerSymbol: 'BTS',\n    value: '$78.54',\n    change: '+4.65',\n    percentageChange: '+6.3%',\n    changeType: 'positive',\n  },\n  {\n    name: 'Gamma Industries',\n    tickerSymbol: 'GMI',\n    value: '$75.68',\n    change: '-5.74',\n    percentageChange: '-7.1%',\n    changeType: 'negative',\n  },\n];\n\nconst sanitizeName = (name: string) => {\n  return name\n    .replace(/\\\\s+/g, '-')\n    .replace(/[^a-zA-Z0-9-]/g, '_')\n    .toLowerCase();\n};\n\nexport default function Stats10() {\n  return (\n    <div className=\"flex w-full items-center justify-center p-10\">\n      <dl className=\"grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3\">\n        {summary.map((item) => {\n          const sanitizedName = sanitizeName(item.name);\n          const gradientId = `gradient-${sanitizedName}`;\n\n          const color =\n            item.changeType === 'positive'\n              ? 'hsl(142.1 76.2% 36.3%)'\n              : 'hsl(0 72.2% 50.6%)';\n\n          return (\n            <Card className=\"overflow-hidden p-0 shadow-2xs\" key={item.name}>\n              <CardContent className=\"p-0\">\n                <div className=\"p-4 pb-0\">\n                  <dt className=\"font-medium text-foreground text-sm\">\n                    {item.name}{' '}\n                    <span className=\"font-normal text-muted-foreground\">\n                      ({item.tickerSymbol})\n                    </span>\n                  </dt>\n                  <div className=\"flex items-baseline justify-between\">\n                    <dd\n                      className={cn(\n                        item.changeType === 'positive'\n                          ? 'text-green-600 dark:text-green-500'\n                          : 'text-red-600 dark:text-red-500',\n                        'font-semibold text-lg'\n                      )}\n                    >\n                      {item.value}\n                    </dd>\n                    <dd className=\"flex items-center space-x-1 text-sm\">\n                      <span className=\"font-medium text-foreground\">\n                        {item.change}\n                      </span>\n                      <span\n                        className={cn(\n                          item.changeType === 'positive'\n                            ? 'text-green-600 dark:text-green-500'\n                            : 'text-red-600 dark:text-red-500'\n                        )}\n                      >\n                        ({item.percentageChange})\n                      </span>\n                    </dd>\n                  </div>\n                </div>\n\n                <div className=\"mt-2 h-16 overflow-hidden\">\n                  <ChartContainer\n                    className=\"h-full w-full\"\n                    config={{\n                      [item.name]: {\n                        label: item.name,\n                        color,\n                      },\n                    }}\n                  >\n                    <AreaChart\n                      data={data}\n                      margin={{ top: 0, right: 0, bottom: 0, left: 0 }}\n                    >\n                      <defs>\n                        <linearGradient\n                          id={gradientId}\n                          x1=\"0\"\n                          x2=\"0\"\n                          y1=\"0\"\n                          y2=\"1\"\n                        >\n                          <stop\n                            offset=\"5%\"\n                            stopColor={color}\n                            stopOpacity={0.3}\n                          />\n                          <stop\n                            offset=\"95%\"\n                            stopColor={color}\n                            stopOpacity={0}\n                          />\n                        </linearGradient>\n                      </defs>\n                      <XAxis dataKey=\"date\" hide={true} />\n                      <Area\n                        dataKey={item.name}\n                        fill={`url(#${gradientId})`}\n                        fillOpacity={0.4}\n                        stroke={color}\n                        strokeWidth={1.5}\n                        type=\"monotone\"\n                      />\n                    </AreaChart>\n                  </ChartContainer>\n                </div>\n              </CardContent>\n            </Card>\n          );\n        })}\n      </dl>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-11",
      "type": "registry:block",
      "title": "Stats Dashboard with Progress Bars",
      "description": "A stats dashboard with progress bars block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "dialog",
        "input",
        "label"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-11.tsx",
          "type": "registry:component",
          "target": "components/stats-11.tsx",
          "content": "'use client';\n\nimport { Box, Edit } from 'lucide-react';\nimport type React from 'react';\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent } from '@/components/ui/card';\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\n\ninterface MetricCardProps {\n  title: string;\n  value: string;\n  limit: string;\n  percentage: number;\n  status?: string;\n  statusColor?: string;\n  progressColor: string;\n  details?: Array<{ label: string; value: string; color: string }>;\n  actionLabel: string;\n  actionIcon: React.ReactNode;\n  warningMessage?: string;\n  onActionClick?: () => void;\n}\n\nfunction MetricCard({\n  title,\n  value,\n  limit,\n  percentage,\n  status,\n  statusColor = 'text-emerald-600 dark:text-emerald-400',\n  progressColor,\n  details,\n  actionLabel,\n  actionIcon,\n  warningMessage,\n  onActionClick,\n}: MetricCardProps) {\n  const renderProgressBar = () => {\n    if (details && title === 'Commands') {\n      const writes = Number.parseInt(details[0].value.replace(/,/g, ''));\n      const reads = Number.parseInt(details[1].value.replace(/,/g, ''));\n      const total = writes + reads;\n      const writesPercentage = (writes / total) * 100;\n      const readsPercentage = (reads / total) * 100;\n\n      return (\n        <div className=\"relative h-1 w-full overflow-hidden rounded-full bg-muted\">\n          <div\n            className=\"absolute left-0 h-full w-full origin-left bg-emerald-500 transition-transform duration-200 ease-out\"\n            style={{ transform: `scaleX(${writesPercentage / 100})` }}\n          />\n          <div\n            className=\"absolute left-0 h-full w-full origin-left bg-blue-500 transition-transform duration-200 ease-out\"\n            style={{\n              transform: `translateX(${writesPercentage}%) scaleX(${readsPercentage / 100})`,\n            }}\n          />\n        </div>\n      );\n    }\n\n    return (\n      <div className=\"relative h-1 w-full overflow-hidden rounded-full bg-muted\">\n        <div\n          className={`h-full w-full origin-left transition-transform duration-200 ease-out ${progressColor}`}\n          style={{ transform: `scaleX(${Math.min(percentage, 100) / 100})` }}\n        />\n      </div>\n    );\n  };\n\n  return (\n    <Card className=\"relative max-w-[280px] overflow-hidden shadow-2xs\">\n      <CardContent className=\"p-4 py-0\">\n        <h5 className=\"font-normal text-muted-foreground text-xs uppercase leading-none tracking-wide dark:text-foreground/80\">\n          {title}\n        </h5>\n\n        <div className=\"mt-2 flex items-baseline gap-1\">\n          <div className=\"font-medium text-[1.2rem] text-foreground tabular-nums leading-none\">\n            {value}\n          </div>\n          <div className=\"text-muted-foreground text-xs leading-none\">\n            / {limit}\n          </div>\n        </div>\n\n        <div className=\"mt-3\">\n          {renderProgressBar()}\n\n          {details && (\n            <div className=\"my-6 mb-8\">\n              <div className=\"flex flex-col gap-3\">\n                {details.map((detail, index) => (\n                  <div\n                    className=\"flex w-full items-center text-muted-foreground text-xs leading-none dark:text-foreground/70\"\n                    key={index}\n                  >\n                    <div\n                      className={`mr-[6px] h-2 w-2 rounded-full ${detail.color}`}\n                    />\n                    <div className=\"mr-1\">{detail.label}</div>\n                    <div className=\"h-[9px] flex-1 border-border border-b-2 border-dotted\" />\n                    <div className=\"ml-1 tabular-nums\">{detail.value}</div>\n                  </div>\n                ))}\n              </div>\n            </div>\n          )}\n\n          {status && (\n            <div className=\"pt-2\">\n              <div className={statusColor}>{status}</div>\n            </div>\n          )}\n\n          {warningMessage && (\n            <div className=\"pt-2\">\n              <div className=\"text-amber-700 text-sm dark:text-amber-400\">\n                {warningMessage}\n              </div>\n            </div>\n          )}\n        </div>\n\n        <div className=\"absolute right-0 bottom-0 left-0\">\n          <Button\n            className=\"h-8 w-full justify-start gap-0 rounded-none bg-muted/50 text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300\"\n            onClick={onActionClick}\n            variant=\"ghost\"\n          >\n            {actionIcon}\n            <span className=\"ml-1 text-xs\">{actionLabel}</span>\n          </Button>\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n\nfunction BudgetDialog({\n  open,\n  onOpenChange,\n}: {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n}) {\n  const [budget, setBudget] = useState('150');\n\n  const handleUpdate = () => {\n    onOpenChange(false);\n  };\n\n  return (\n    <Dialog onOpenChange={onOpenChange} open={open}>\n      <DialogContent className=\"sm:max-w-md\">\n        <DialogHeader>\n          <DialogTitle>Update budget</DialogTitle>\n          <DialogDescription>\n            When your monthly cost reaches the max budget, we send an email and\n            throttle your database. You will not be charged beyond your set\n            budget for this database.\n          </DialogDescription>\n        </DialogHeader>\n\n        <div className=\"space-y-2\">\n          <Label htmlFor=\"budget\">Max budget per month</Label>\n          <Input\n            id=\"budget\"\n            onChange={(e) => setBudget(e.target.value)}\n            placeholder=\"150\"\n            type=\"number\"\n            value={budget}\n          />\n        </div>\n\n        <DialogFooter className=\"pt-2\">\n          <Button onClick={() => onOpenChange(false)} variant=\"outline\">\n            Cancel\n          </Button>\n          <Button onClick={handleUpdate}>Update</Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n\nexport default function Stats11() {\n  const [budgetDialogOpen, setBudgetDialogOpen] = useState(false);\n\n  return (\n    <>\n      <div className=\"grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-4\">\n        <MetricCard\n          actionIcon={<Box className=\"h-4 w-4\" />}\n          actionLabel=\"Upgrade\"\n          details={[\n            { label: 'Writes', value: '11,276,493', color: 'bg-emerald-500' },\n            { label: 'Reads', value: '2,548,921', color: 'bg-blue-500' },\n          ]}\n          limit=\"Unlimited\"\n          percentage={67}\n          progressColor=\"bg-blue-500\"\n          title=\"Commands\"\n          value=\"13.8M\"\n        />\n\n        <MetricCard\n          actionIcon={<Box className=\"h-4 w-4\" />}\n          actionLabel=\"Upgrade\"\n          limit=\"150 GB\"\n          percentage={94}\n          progressColor=\"bg-orange-500\"\n          title=\"Bandwidth\"\n          value=\"141 GB\"\n          warningMessage=\"There will be a charge for the excessive bandwidth over the limit.\"\n        />\n\n        <MetricCard\n          actionIcon={<Box className=\"h-4 w-4\" />}\n          actionLabel=\"Upgrade\"\n          limit=\"500 GB\"\n          percentage={7.4}\n          progressColor=\"bg-emerald-500\"\n          status=\"It's all right.\"\n          title=\"Storage\"\n          value=\"37 GB\"\n        />\n\n        <MetricCard\n          actionIcon={<Edit className=\"h-4 w-4\" />}\n          actionLabel=\"Change Budget\"\n          limit=\"$150 Budget\"\n          onActionClick={() => setBudgetDialogOpen(true)}\n          percentage={48.95}\n          progressColor=\"bg-emerald-500\"\n          status=\"It's all right.\"\n          title=\"Cost\"\n          value=\"$73.42\"\n        />\n      </div>\n\n      <BudgetDialog\n        onOpenChange={setBudgetDialogOpen}\n        open={budgetDialogOpen}\n      />\n    </>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-12",
      "type": "registry:block",
      "title": "Stats Usage Dashboard",
      "description": "A stats usage dashboard block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "card",
        "chart"
      ],
      "dependencies": [
        "recharts"
      ],
      "files": [
        {
          "path": "content/components/stats/stats-12.tsx",
          "type": "registry:component",
          "target": "components/stats-12.tsx",
          "content": "'use client';\n\nimport { Cell, Pie, PieChart } from 'recharts';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent, CardHeader } from '@/components/ui/card';\nimport { type ChartConfig, ChartContainer } from '@/components/ui/chart';\n\ninterface UsageItem {\n  name: string;\n  current: string;\n  limit: string;\n  percentage: number;\n  href?: string;\n}\n\nconst usageData: UsageItem[] = [\n  { name: 'ISR Reads', current: '358K', limit: '1M', percentage: 35.8 },\n  { name: 'Edge Requests', current: '317K', limit: '1M', percentage: 31.7 },\n  {\n    name: 'Fast Origin Transfer',\n    current: '3.07 GB',\n    limit: '10 GB',\n    percentage: 30.7,\n  },\n  {\n    name: 'Speed Insights Data Points',\n    current: '791',\n    limit: '10K',\n    percentage: 7.9,\n  },\n  {\n    name: 'Fast Data Transfer',\n    current: '4.98 GB',\n    limit: '100 GB',\n    percentage: 5.0,\n  },\n  {\n    name: 'Function Duration',\n    current: '3.1 GB-Hrs',\n    limit: '100 GB-Hrs',\n    percentage: 3.1,\n  },\n  {\n    name: 'Web Analytics Events',\n    current: '1.3K',\n    limit: '50K',\n    percentage: 2.6,\n  },\n  { name: 'ISR Writes', current: '4.8K', limit: '200K', percentage: 2.4 },\n  {\n    name: 'Function Invocations',\n    current: '19K',\n    limit: '1M',\n    percentage: 1.9,\n  },\n  {\n    name: 'Image Optimization - Cache Reads',\n    current: '4.3K',\n    limit: '300K',\n    percentage: 1.4,\n  },\n];\n\nconst chartConfig = {\n  used: {\n    label: 'Used',\n    color: 'hsl(var(--primary))',\n  },\n  remaining: {\n    label: 'Remaining',\n    color: 'hsl(var(--muted))',\n  },\n} satisfies ChartConfig;\n\nfunction DonutChart({ percentage }: { percentage: number }) {\n  const backgroundData = [{ name: 'background', value: 100, fill: '#E5E7EB' }];\n  const foregroundData = [\n    {\n      name: 'used',\n      value: Math.max(0, Math.min(100, Number(percentage))),\n      fill: '#3B82F6',\n    },\n    {\n      name: 'empty',\n      value: 100 - Math.max(0, Math.min(100, Number(percentage))),\n      fill: 'transparent',\n    },\n  ];\n\n  return (\n    <ChartContainer\n      className=\"aspect-square h-6 w-6 shrink-0\"\n      config={chartConfig}\n    >\n      <PieChart>\n        <Pie\n          cx=\"50%\"\n          cy=\"50%\"\n          data={backgroundData}\n          dataKey=\"value\"\n          innerRadius={6}\n          isAnimationActive={false}\n          nameKey=\"name\"\n          outerRadius={10}\n        >\n          {backgroundData.map((entry, index) => (\n            <Cell fill={entry.fill} key={`bg-cell-${index}`} />\n          ))}\n        </Pie>\n        <Pie\n          cx=\"50%\"\n          cy=\"50%\"\n          data={foregroundData}\n          dataKey=\"value\"\n          endAngle={-270}\n          innerRadius={6}\n          nameKey=\"name\"\n          outerRadius={10}\n          startAngle={90}\n        >\n          {foregroundData.map((entry, index) => (\n            <Cell fill={entry.fill} key={`fg-cell-${index}`} />\n          ))}\n        </Pie>\n      </PieChart>\n    </ChartContainer>\n  );\n}\n\nexport default function Stats12() {\n  return (\n    <Card className=\"w-full max-w-md gap-3 py-5 shadow-2xs\">\n      <CardHeader className=\"px-5\">\n        <div className=\"flex items-center justify-between\">\n          <div className=\"flex flex-col\">\n            <h3 className=\"text-balance font-medium text-sm\">Last 30 days</h3>\n            <p className=\"text-pretty font-medium text-muted-foreground text-xs\">\n              Updated just now\n            </p>\n          </div>\n          <Button className=\"h-6 font-medium text-xs\" size=\"sm\">\n            Upgrade\n          </Button>\n        </div>\n      </CardHeader>\n\n      <CardContent className=\"px-3 pt-0\">\n        <div className=\"space-y-0\">\n          {usageData.map((item, index) => (\n            <div\n              className={`flex items-center gap-3 rounded-sm p-2 transition-colors hover:bg-muted/50 ${\n                index % 2 === 1 ? 'bg-muted/20' : ''\n              }`}\n              key={item.name}\n            >\n              <DonutChart percentage={item.percentage} />\n              <span className=\"flex-1 truncate text-sm leading-4\">\n                {item.name}\n              </span>\n              <span className=\"font-medium text-muted-foreground text-xs tabular-nums tracking-tighter\">\n                {item.current} /{' '}\n                <span className=\"text-foreground\">{item.limit}</span>\n              </span>\n            </div>\n          ))}\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-13",
      "type": "registry:block",
      "title": "Stats with Segmented Progress",
      "description": "A stats with segmented progress block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "card"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/stats/stats-13.tsx",
          "type": "registry:component",
          "target": "components/stats-13.tsx",
          "content": "'use client';\n\nimport { Card, CardContent } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\ninterface Props {\n  title?: string;\n  used?: number;\n  total?: number;\n  usedLabel?: string;\n  totalLabel?: string;\n  segments?: {\n    label: string;\n    value: number;\n    color: string;\n  }[];\n  className?: string;\n}\n\nconst defaultSegments: NonNullable<Props['segments']> = [\n  { label: 'Documents', value: 2400, color: 'bg-blue-500' },\n  { label: 'Photos', value: 1800, color: 'bg-emerald-500' },\n  { label: 'Videos', value: 3200, color: 'bg-amber-500' },\n  { label: 'Music', value: 900, color: 'bg-purple-500' },\n];\n\nexport default function Stats13({\n  title = 'Using Storage',\n  used = 8300,\n  total = 15,\n  usedLabel = 'MB',\n  totalLabel = 'GB',\n  segments = defaultSegments,\n  className,\n}: Props) {\n  const totalValue = total * 1000;\n  const freeValue = totalValue - used;\n\n  return (\n    <Card className={cn('w-full max-w-4xl shadow-sm', className)}>\n      <CardContent className=\"py-0\">\n        <p className=\"mb-4 text-pretty text-base text-muted-foreground\">\n          {title}{' '}\n          <span className=\"font-semibold text-foreground tabular-nums\">\n            {used.toLocaleString(undefined, {\n              minimumFractionDigits: 0,\n              maximumFractionDigits: 2,\n            })}{' '}\n            {usedLabel}\n          </span>{' '}\n          of {total} {totalLabel}\n        </p>\n\n        <div className=\"mb-4 flex h-2.5 w-full overflow-hidden rounded-full bg-muted\">\n          {segments.map((segment) => {\n            const percentage = (segment.value / totalValue) * 100;\n            return (\n              <div\n                aria-label={segment.label}\n                aria-valuemax={totalValue}\n                aria-valuemin={0}\n                aria-valuenow={segment.value}\n                className={cn('h-full', segment.color)}\n                key={segment.label}\n                role=\"progressbar\"\n                style={{ width: `${percentage}%` }}\n              />\n            );\n          })}\n        </div>\n\n        <div className=\"flex flex-wrap items-center gap-x-8 gap-y-2\">\n          {segments.map((segment) => (\n            <div className=\"flex items-center gap-2\" key={segment.label}>\n              <span\n                aria-hidden=\"true\"\n                className={cn('size-3 shrink-0 rounded', segment.color)}\n              />\n              <span className=\"text-muted-foreground text-sm\">\n                {segment.label}\n              </span>\n              <span className=\"text-muted-foreground text-sm tabular-nums\">\n                {Math.round(segment.value)}\n                {usedLabel}\n              </span>\n            </div>\n          ))}\n          <div className=\"flex items-center gap-2\">\n            <span\n              aria-hidden=\"true\"\n              className=\"size-3 shrink-0 rounded-sm bg-muted\"\n            />\n            <span className=\"text-muted-foreground text-sm\">Free</span>\n            <span className=\"text-muted-foreground text-sm tabular-nums\">\n              {Math.round(freeValue)}\n              {usedLabel}\n            </span>\n          </div>\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-14",
      "type": "registry:block",
      "title": "Stats with Usage Breakdown",
      "description": "A stats with usage breakdown block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "card"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/stats/stats-14.tsx",
          "type": "registry:component",
          "target": "components/stats-14.tsx",
          "content": "import { Badge } from '@/components/ui/badge';\nimport { Card, CardContent } from '@/components/ui/card';\n\ninterface UsageItem {\n  label: string;\n  amount: number;\n  percentage: number;\n  color: 'emerald' | 'amber' | 'rose';\n}\n\nconst data: UsageItem[] = [\n  { label: 'Compute', amount: 450, percentage: 52.3, color: 'emerald' },\n  { label: 'Storage', amount: 285, percentage: 33.1, color: 'amber' },\n  { label: 'Bandwidth', amount: 125, percentage: 14.6, color: 'rose' },\n];\n\nconst colorClasses = {\n  emerald: 'bg-emerald-500 dark:bg-emerald-400',\n  amber: 'bg-amber-500 dark:bg-amber-400',\n  rose: 'bg-rose-500 dark:bg-rose-400',\n};\n\nexport function Stats14() {\n  return (\n    <Card className=\"w-full max-w-sm shadow-none\">\n      <CardContent className=\"flex flex-col justify-between pt-0\">\n        <div>\n          <div className=\"flex items-center gap-2\">\n            <h3 className=\"text-balance font-bold text-foreground text-sm\">\n              Usage\n            </h3>\n            <Badge\n              className=\"bg-amber-50 text-amber-700 ring-1 ring-amber-500/30 dark:bg-amber-400/10 dark:text-amber-300 dark:ring-amber-400/20\"\n              variant=\"secondary\"\n            >\n              +12.5%\n            </Badge>\n          </div>\n\n          <p className=\"mt-2 flex items-baseline gap-2 text-pretty\">\n            <span className=\"text-foreground text-xl\">$860</span>\n            <span className=\"text-muted-foreground text-sm\">this month</span>\n          </p>\n\n          <div className=\"mt-4\">\n            <p className=\"text-pretty font-medium text-foreground text-sm\">\n              Resource breakdown\n            </p>\n            <div className=\"mt-2 flex items-center gap-0.5\">\n              {data.map((item, index) => (\n                <div\n                  className={`${colorClasses[item.color]} h-1.5 rounded-xs`}\n                  key={index}\n                  style={{ width: `${item.percentage}%` }}\n                />\n              ))}\n            </div>\n          </div>\n\n          <ul className=\"mt-5 space-y-2\" role=\"list\">\n            {data.map((item, index) => (\n              <li className=\"flex items-center gap-2 text-xs\" key={index}>\n                <span\n                  aria-hidden=\"true\"\n                  className={`${colorClasses[item.color]} size-2.5 rounded-xs`}\n                />\n                <span className=\"text-foreground\">{item.label}</span>\n                <span className=\"text-muted-foreground\">\n                  (${item.amount} / {item.percentage}%)\n                </span>\n              </li>\n            ))}\n          </ul>\n        </div>\n\n        <p className=\"mt-6 text-pretty text-muted-foreground text-xs\">\n          Configure limits in{' '}\n          <a\n            className=\"text-emerald-600 hover:underline dark:text-emerald-400\"\n            href=\"#\"\n          >\n            resource settings.\n          </a>\n        </p>\n      </CardContent>\n    </Card>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "stats-15",
      "type": "registry:block",
      "title": "Stats with Value Breakdown",
      "description": "A stats with value breakdown block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/stats/stats-15.tsx",
          "type": "registry:component",
          "target": "components/stats-15.tsx",
          "content": "import { cn } from '@/lib/utils';\n\nconst data = [\n  {\n    label: 'After 1 year',\n    value: '$2,400',\n    percentage: '+8.2%',\n  },\n  {\n    label: 'After 5 years',\n    value: '$14,800',\n    percentage: '+24.6%',\n  },\n  {\n    label: 'After 10 years',\n    value: '$38,500',\n    percentage: '+52.1%',\n  },\n];\n\nexport function Stats15() {\n  return (\n    <div className=\"w-full max-w-2xs\">\n      <h3 className=\"text-balance font-medium text-foreground text-sm\">\n        Investment growth projection\n      </h3>\n      <ul className=\"mt-2 divide-y divide-border text-sm\" role=\"list\">\n        {data.map((item, index) => (\n          <li className=\"flex items-center justify-between py-3\" key={index}>\n            <span className=\"text-muted-foreground\">{item.label}</span>\n            <span className=\"flex items-center gap-3 tabular-nums\">\n              <span className=\"text-right font-medium text-foreground\">\n                {item.value}\n              </span>\n              <span aria-hidden=\"true\" className=\"h-5 w-px bg-border\" />\n              <span\n                className={cn(\n                  'w-15 rounded px-1.5 py-1 text-center font-semibold text-xs',\n                  'bg-emerald-50 text-emerald-600 dark:bg-emerald-400/10 dark:text-emerald-400'\n                )}\n              >\n                {item.percentage}\n              </span>\n            </span>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "stats"
      ]
    },
    {
      "name": "table-01",
      "type": "registry:block",
      "title": "Table with Accordion",
      "description": "A table with accordion block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "button",
        "collapsible",
        "table"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/tables/table-01.tsx",
          "type": "registry:component",
          "target": "components/table-01.tsx",
          "content": "'use client';\n\nimport { ChevronDown, ChevronRight } from 'lucide-react';\nimport { useState } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Collapsible, CollapsibleContent } from '@/components/ui/collapsible';\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '@/components/ui/table';\nimport { cn } from '@/lib/utils';\n\ninterface TableRowData {\n  id: string;\n  name: string;\n  category: string;\n  value: number;\n  date: string;\n  children?: TableRowData[];\n}\n\nconst sampleData: TableRowData[] = [\n  {\n    id: '001',\n    name: 'Project Alpha',\n    category: 'Development',\n    value: 45_000,\n    date: '2024-01-15',\n    children: [\n      {\n        id: '001-01',\n        name: 'Frontend Module',\n        category: 'Development',\n        value: 15_000,\n        date: '2024-01-16',\n      },\n      {\n        id: '001-02',\n        name: 'Backend Module',\n        category: 'Development',\n        value: 20_000,\n        date: '2024-01-21',\n      },\n      {\n        id: '001-03',\n        name: 'Testing Suite',\n        category: 'Development',\n        value: 10_000,\n        date: '2024-01-24',\n      },\n    ],\n  },\n  {\n    id: '002',\n    name: 'Marketing Campaign',\n    category: 'Marketing',\n    value: 28_500,\n    date: '2024-01-18',\n    children: [\n      {\n        id: '002-01',\n        name: 'Social Media',\n        category: 'Marketing',\n        value: 12_000,\n        date: '2024-01-19',\n      },\n      {\n        id: '002-02',\n        name: 'Email Marketing',\n        category: 'Marketing',\n        value: 8500,\n        date: '2024-01-22',\n      },\n      {\n        id: '002-03',\n        name: 'SEO Optimization',\n        category: 'Marketing',\n        value: 8000,\n        date: '2024-01-23',\n      },\n    ],\n  },\n  {\n    id: '003',\n    name: 'Infrastructure Upgrade',\n    category: 'Operations',\n    value: 67_200,\n    date: '2024-01-20',\n    children: [\n      {\n        id: '003-01',\n        name: 'Cloud Migration',\n        category: 'Operations',\n        value: 35_000,\n        date: '2024-01-21',\n      },\n      {\n        id: '003-02',\n        name: 'Security Enhancement',\n        category: 'Operations',\n        value: 32_200,\n        date: '2024-01-24',\n      },\n    ],\n  },\n  {\n    id: '004',\n    name: 'Customer Support',\n    category: 'Service',\n    value: 19_800,\n    date: '2024-01-25',\n  },\n];\n\ninterface AccordionRowProps {\n  row: TableRowData;\n  defaultOpen?: boolean;\n}\n\nfunction AccordionRow({ row, defaultOpen = false }: AccordionRowProps) {\n  const [isOpen, setIsOpen] = useState(defaultOpen);\n  const hasChildren = row.children && row.children.length > 0;\n\n  return (\n    <Collapsible\n      onOpenChange={setIsOpen}\n      open={isOpen}\n      render={\n        <TableBody className=\"[&_tr:last-child]:border-b last:[&_tr:last-child]:border-0\" />\n      }\n    >\n      <TableRow\n        className={cn(\n          'grid grid-cols-[40px_80px_180px_110px_100px_110px] bg-muted/50 hover:bg-muted/50',\n          isOpen && 'border-b-0'\n        )}\n      >\n        <TableCell className=\"p-0\">\n          <Button\n            aria-label={isOpen ? 'Collapse row' : 'Expand row'}\n            className={cn(\n              'h-full w-full rounded-none p-3 text-muted-foreground transition-colors',\n              hasChildren && 'hover:bg-transparent hover:text-foreground',\n              !hasChildren && 'cursor-default opacity-30'\n            )}\n            disabled={!hasChildren}\n            onClick={() => setIsOpen(!isOpen)}\n            size=\"icon\"\n            variant=\"ghost\"\n          >\n            {hasChildren ? (\n              isOpen ? (\n                <ChevronDown className=\"h-4 w-4 transition-transform duration-200\" />\n              ) : (\n                <ChevronRight className=\"h-4 w-4 transition-transform duration-200\" />\n              )\n            ) : (\n              <div className=\"h-4 w-4\" />\n            )}\n          </Button>\n        </TableCell>\n        <TableCell className=\"p-3 font-medium font-mono text-muted-foreground text-sm\">\n          {row.id}\n        </TableCell>\n        <TableCell className=\"p-3 font-medium text-sm\">{row.name}</TableCell>\n        <TableCell className=\"p-3 text-muted-foreground text-sm\">\n          {row.category}\n        </TableCell>\n        <TableCell className=\"p-3 text-right font-mono font-semibold text-sm tabular-nums\">\n          ${row.value.toLocaleString()}\n        </TableCell>\n        <TableCell className=\"p-3 text-muted-foreground text-sm\">\n          {row.date}\n        </TableCell>\n      </TableRow>\n\n      {hasChildren && (\n        <TableRow className=\"grid grid-cols-[40px_80px_180px_110px_100px_110px] border-b-0 hover:bg-transparent\">\n          <TableCell className=\"col-span-6 p-0\" colSpan={6}>\n            <CollapsibleContent>\n              <div className=\"w-full border-border border-b bg-muted/20\">\n                <Table>\n                  <TableHeader>\n                    <TableRow className=\"grid grid-cols-[40px_80px_180px_110px_100px_110px] border-b-0 bg-muted/30\">\n                      <TableHead className=\"flex h-7 items-center border-border border-y px-3 py-1.5\" />\n                      <TableHead className=\"flex h-7 items-center border-border border-y px-3 py-1.5 text-xs\">\n                        ID\n                      </TableHead>\n                      <TableHead className=\"flex h-7 items-center border-border border-y px-3 py-1.5 text-xs\">\n                        Name\n                      </TableHead>\n                      <TableHead className=\"flex h-7 items-center border-border border-y px-3 py-1.5 text-xs\">\n                        Category\n                      </TableHead>\n                      <TableHead className=\"flex h-7 items-center justify-end border-border border-y px-3 py-1.5 text-right text-xs\">\n                        Value\n                      </TableHead>\n                      <TableHead className=\"flex h-7 items-center border-border border-y px-3 py-1.5 text-xs\">\n                        Date\n                      </TableHead>\n                    </TableRow>\n                  </TableHeader>\n                  <TableBody>\n                    {row.children?.map((childRow) => (\n                      <TableRow\n                        className=\"grid grid-cols-[40px_80px_180px_110px_100px_110px]\"\n                        key={childRow.id}\n                      >\n                        <TableCell className=\"px-3 py-2\" />\n                        <TableCell className=\"px-3 py-2 font-mono text-muted-foreground text-xs tabular-nums\">\n                          {childRow.id}\n                        </TableCell>\n                        <TableCell className=\"px-3 py-2 font-medium text-xs\">\n                          {childRow.name}\n                        </TableCell>\n                        <TableCell className=\"px-3 py-2 text-muted-foreground text-xs\">\n                          {childRow.category}\n                        </TableCell>\n                        <TableCell className=\"px-3 py-2 text-right font-mono font-semibold text-xs tabular-nums\">\n                          ${childRow.value.toLocaleString()}\n                        </TableCell>\n                        <TableCell className=\"px-3 py-2 text-muted-foreground text-xs\">\n                          {childRow.date}\n                        </TableCell>\n                      </TableRow>\n                    ))}\n                  </TableBody>\n                </Table>\n              </div>\n            </CollapsibleContent>\n          </TableCell>\n        </TableRow>\n      )}\n    </Collapsible>\n  );\n}\n\nexport default function Table01() {\n  return (\n    <div className=\"max-w-fit overflow-hidden rounded-lg border border-border bg-card shadow-sm\">\n      <div className=\"overflow-x-auto\">\n        <Table>\n          <TableHeader>\n            <TableRow className=\"grid grid-cols-[40px_80px_180px_110px_100px_110px] bg-muted/50\">\n              <TableHead className=\"p-3\" />\n              <TableHead className=\"p-3 font-semibold text-foreground text-sm\">\n                ID\n              </TableHead>\n              <TableHead className=\"p-3 font-semibold text-foreground text-sm\">\n                Name\n              </TableHead>\n              <TableHead className=\"p-3 font-semibold text-foreground text-sm\">\n                Category\n              </TableHead>\n              <TableHead className=\"p-3 text-right font-semibold text-foreground text-sm\">\n                Value\n              </TableHead>\n              <TableHead className=\"p-3 font-semibold text-foreground text-sm\">\n                Date\n              </TableHead>\n            </TableRow>\n          </TableHeader>\n          {sampleData.map((row, index) => (\n            <AccordionRow defaultOpen={index === 0} key={row.id} row={row} />\n          ))}\n        </Table>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "tables"
      ]
    },
    {
      "name": "table-02",
      "type": "registry:block",
      "title": "Table with Actions",
      "description": "A table with actions block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "button",
        "table",
        "tooltip"
      ],
      "dependencies": [
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/tables/table-02.tsx",
          "type": "registry:component",
          "target": "components/table-02.tsx",
          "content": "'use client';\n\nimport {\n  CheckCircle,\n  FileTextIcon,\n  Loader2,\n  PauseIcon,\n  PlayIcon,\n  Trash2Icon,\n} from 'lucide-react';\nimport { useState } from 'react';\n\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '@/components/ui/table';\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from '@/components/ui/tooltip';\n\ninterface Task {\n  id: string;\n  title: string;\n  assignee: string;\n  status: 'pending' | 'in-progress' | 'completed' | 'blocked';\n  priority: 'low' | 'medium' | 'high' | 'urgent';\n  dueDate: string;\n  notes: string;\n}\n\ntype TaskActionType = 'start' | 'pause' | 'complete' | 'delete' | 'view';\n\nconst tasks: Task[] = [\n  {\n    id: 'TASK-001',\n    title: 'Implement User Authentication',\n    assignee: 'Sarah Chen',\n    status: 'in-progress',\n    priority: 'high',\n    dueDate: '2024-03-25',\n    notes: 'OAuth 2.0 integration with Google and GitHub providers',\n  },\n  {\n    id: 'TASK-002',\n    title: 'Design Dashboard UI',\n    assignee: 'Michael Torres',\n    status: 'completed',\n    priority: 'medium',\n    dueDate: '2024-03-20',\n    notes: 'Finalize dashboard layout with responsive grid system',\n  },\n  {\n    id: 'TASK-003',\n    title: 'API Performance Optimization',\n    assignee: 'Emma Rodriguez',\n    status: 'pending',\n    priority: 'urgent',\n    dueDate: '2024-03-22',\n    notes: 'Reduce API response time by implementing caching strategy',\n  },\n  {\n    id: 'TASK-004',\n    title: 'Write Unit Tests',\n    assignee: 'James Wilson',\n    status: 'in-progress',\n    priority: 'medium',\n    dueDate: '2024-03-28',\n    notes: 'Achieve 80% code coverage for authentication module',\n  },\n  {\n    id: 'TASK-005',\n    title: 'Database Migration',\n    assignee: 'Olivia Martinez',\n    status: 'blocked',\n    priority: 'high',\n    dueDate: '2024-03-24',\n    notes: 'Waiting for infrastructure team approval before proceeding',\n  },\n  {\n    id: 'TASK-006',\n    title: 'Update Documentation',\n    assignee: 'Lucas Anderson',\n    status: 'pending',\n    priority: 'low',\n    dueDate: '2024-03-30',\n    notes: 'Document new API endpoints and authentication flow',\n  },\n  {\n    id: 'TASK-007',\n    title: 'Security Audit',\n    assignee: 'Sophia Taylor',\n    status: 'completed',\n    priority: 'urgent',\n    dueDate: '2024-03-19',\n    notes:\n      'Conducted comprehensive security review and vulnerability assessment',\n  },\n];\n\nfunction getStatusBadge(status: Task['status']) {\n  switch (status) {\n    case 'pending':\n      return (\n        <Badge\n          className=\"border-0 bg-amber-500/15 text-amber-700 hover:bg-amber-500/25 dark:bg-amber-500/10 dark:text-amber-300 dark:hover:bg-amber-500/20\"\n          variant=\"outline\"\n        >\n          Pending\n        </Badge>\n      );\n    case 'in-progress':\n      return (\n        <Badge\n          className=\"border-0 bg-blue-500/15 text-blue-700 hover:bg-blue-500/25 dark:bg-blue-500/10 dark:text-blue-400 dark:hover:bg-blue-500/20\"\n          variant=\"outline\"\n        >\n          In Progress\n        </Badge>\n      );\n    case 'completed':\n      return (\n        <Badge\n          className=\"border-0 bg-green-500/15 text-green-700 hover:bg-green-500/25 dark:bg-green-500/10 dark:text-green-400 dark:hover:bg-green-500/20\"\n          variant=\"outline\"\n        >\n          Completed\n        </Badge>\n      );\n    case 'blocked':\n      return (\n        <Badge\n          className=\"border-0 bg-rose-500/15 text-rose-700 hover:bg-rose-500/25 dark:bg-rose-500/10 dark:text-rose-400 dark:hover:bg-rose-500/20\"\n          variant=\"outline\"\n        >\n          Blocked\n        </Badge>\n      );\n    default:\n      return <Badge variant=\"secondary\">{status}</Badge>;\n  }\n}\n\nexport default function Table02() {\n  const [pendingAction, setPendingAction] = useState<{\n    id: string;\n    type: TaskActionType;\n  } | null>(null);\n\n  const isTaskActionPending = (action: TaskActionType, taskId: string) =>\n    pendingAction?.id === taskId && pendingAction.type === action;\n\n  const isTaskBusy = (taskId: string) => pendingAction?.id === taskId;\n\n  const handleAction = (task: Task, actionType: TaskActionType) => {\n    setPendingAction({ id: task.id, type: actionType });\n    setTimeout(() => {\n      setPendingAction(null);\n      console.log(`Action \"${actionType}\" completed for task:`, task.title);\n    }, 1000);\n  };\n\n  const renderTaskRow = (task: Task) => {\n    const busy = isTaskBusy(task.id);\n    const startPending = isTaskActionPending('start', task.id);\n    const pausePending = isTaskActionPending('pause', task.id);\n    const completePending = isTaskActionPending('complete', task.id);\n    const deletePending = isTaskActionPending('delete', task.id);\n\n    return (\n      <TableRow className=\"hover:bg-muted/50\" key={task.id}>\n        <TableCell className=\"h-16 px-4 font-medium\">{task.title}</TableCell>\n        <TableCell className=\"h-16 px-4 text-muted-foreground text-sm\">\n          {task.assignee}\n        </TableCell>\n        <TableCell className=\"h-16 px-4\">\n          {getStatusBadge(task.status)}\n        </TableCell>\n\n        <TableCell className=\"h-16 px-4 text-muted-foreground text-sm\">\n          {task.dueDate}\n        </TableCell>\n        <TableCell className=\"h-16 max-w-[300px] px-4 text-muted-foreground text-sm\">\n          <TooltipProvider>\n            <Tooltip>\n              <TooltipTrigger\n                render={<span className=\"block cursor-help truncate\" />}\n              >\n                {task.notes}\n              </TooltipTrigger>\n              <TooltipContent className=\"max-w-md\">{task.notes}</TooltipContent>\n            </Tooltip>\n          </TooltipProvider>\n        </TableCell>\n        <TableCell className=\"h-16 px-4\">\n          <TooltipProvider>\n            <div className=\"flex items-center gap-1\">\n              {(task.status === 'pending' || task.status === 'blocked') && (\n                <Tooltip>\n                  <TooltipTrigger\n                    render={\n                      <Button\n                        aria-label=\"Start\"\n                        className=\"h-8 w-8\"\n                        disabled={busy}\n                        onClick={() => handleAction(task, 'start')}\n                        size=\"icon\"\n                        variant=\"outline\"\n                      />\n                    }\n                  >\n                    {startPending ? (\n                      <Loader2 className=\"size-4 animate-spin\" />\n                    ) : (\n                      <PlayIcon className=\"size-4\" />\n                    )}\n                  </TooltipTrigger>\n                  <TooltipContent>Start</TooltipContent>\n                </Tooltip>\n              )}\n              {task.status === 'in-progress' && (\n                <>\n                  <Tooltip>\n                    <TooltipTrigger\n                      render={\n                        <Button\n                          aria-label=\"Pause\"\n                          className=\"h-8 w-8\"\n                          disabled={busy}\n                          onClick={() => handleAction(task, 'pause')}\n                          size=\"icon\"\n                          variant=\"outline\"\n                        />\n                      }\n                    >\n                      {pausePending ? (\n                        <Loader2 className=\"size-4 animate-spin\" />\n                      ) : (\n                        <PauseIcon className=\"size-4\" />\n                      )}\n                    </TooltipTrigger>\n                    <TooltipContent>Pause</TooltipContent>\n                  </Tooltip>\n                  <Tooltip>\n                    <TooltipTrigger\n                      render={\n                        <Button\n                          aria-label=\"Complete\"\n                          className=\"h-8 w-8\"\n                          disabled={busy}\n                          onClick={() => handleAction(task, 'complete')}\n                          size=\"icon\"\n                          variant=\"outline\"\n                        />\n                      }\n                    >\n                      {completePending ? (\n                        <Loader2 className=\"size-4 animate-spin\" />\n                      ) : (\n                        <CheckCircle className=\"size-4\" />\n                      )}\n                    </TooltipTrigger>\n                    <TooltipContent>Complete</TooltipContent>\n                  </Tooltip>\n                </>\n              )}\n              <Tooltip>\n                <TooltipTrigger\n                  render={\n                    <Button\n                      aria-label=\"Delete\"\n                      className=\"h-8 w-8 text-destructive hover:bg-destructive hover:text-white\"\n                      disabled={busy}\n                      onClick={() => handleAction(task, 'delete')}\n                      size=\"icon\"\n                      variant=\"outline\"\n                    />\n                  }\n                >\n                  {deletePending ? (\n                    <Loader2 className=\"size-4 animate-spin\" />\n                  ) : (\n                    <Trash2Icon className=\"size-4\" />\n                  )}\n                </TooltipTrigger>\n                <TooltipContent>Delete</TooltipContent>\n              </Tooltip>\n              <Tooltip>\n                <TooltipTrigger\n                  render={\n                    <Button\n                      aria-label=\"View details\"\n                      className=\"h-8 w-8\"\n                      disabled={busy}\n                      onClick={() => handleAction(task, 'view')}\n                      size=\"icon\"\n                      variant=\"outline\"\n                    />\n                  }\n                >\n                  <FileTextIcon className=\"size-4\" />\n                </TooltipTrigger>\n                <TooltipContent>View Details</TooltipContent>\n              </Tooltip>\n            </div>\n          </TooltipProvider>\n        </TableCell>\n      </TableRow>\n    );\n  };\n\n  return (\n    <div className=\"w-[95%] rounded-lg border bg-card\">\n      <Table>\n        <TableHeader>\n          <TableRow className=\"border-b hover:bg-transparent\">\n            <TableHead className=\"h-12 px-4 font-medium\">Title</TableHead>\n            <TableHead className=\"h-12 px-4 font-medium\">Assignee</TableHead>\n            <TableHead className=\"h-12 w-[120px] px-4 font-medium\">\n              Status\n            </TableHead>\n\n            <TableHead className=\"h-12 px-4 font-medium\">Due Date</TableHead>\n            <TableHead className=\"h-12 px-4 font-medium\">Notes</TableHead>\n            <TableHead className=\"h-12 w-[180px] px-4 font-medium\">\n              Actions\n            </TableHead>\n          </TableRow>\n        </TableHeader>\n        <TableBody>{tasks.map(renderTaskRow)}</TableBody>\n      </Table>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "tables"
      ]
    },
    {
      "name": "table-03",
      "type": "registry:block",
      "title": "Table with Filters",
      "description": "A table with filters block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "select",
        "table"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/tables/table-03.tsx",
          "type": "registry:component",
          "target": "components/table-03.tsx",
          "content": "'use client';\n\nimport { useState } from 'react';\nimport { Badge } from '@/components/ui/badge';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '@/components/ui/table';\nimport { cn } from '@/lib/utils';\n\ntype Status = 'active' | 'pending' | 'discontinued' | 'on-hold';\n\ninterface Product {\n  sku: string;\n  productName: string;\n  stockLevel: number;\n  category: string;\n  status: Status;\n  unitPrice: string;\n  lastRestocked: string;\n}\n\nconst data: Product[] = [\n  {\n    sku: 'SKU-8472',\n    productName: 'Wireless Mouse Pro',\n    stockLevel: 245,\n    category: 'Electronics',\n    status: 'active',\n    unitPrice: '$24.99',\n    lastRestocked: 'Oct 15, 2024',\n  },\n  {\n    sku: 'SKU-3391',\n    productName: 'Ergonomic Keyboard',\n    stockLevel: 89,\n    category: 'Electronics',\n    status: 'active',\n    unitPrice: '$79.99',\n    lastRestocked: 'Oct 18, 2024',\n  },\n  {\n    sku: 'SKU-7156',\n    productName: 'Office Chair Deluxe',\n    stockLevel: 12,\n    category: 'Furniture',\n    status: 'pending',\n    unitPrice: '$299.99',\n    lastRestocked: 'Oct 12, 2024',\n  },\n  {\n    sku: 'SKU-9204',\n    productName: 'USB-C Hub Adapter',\n    stockLevel: 456,\n    category: 'Accessories',\n    status: 'active',\n    unitPrice: '$34.50',\n    lastRestocked: 'Oct 19, 2024',\n  },\n  {\n    sku: 'SKU-1638',\n    productName: 'Standing Desk Frame',\n    stockLevel: 5,\n    category: 'Furniture',\n    status: 'on-hold',\n    unitPrice: '$449.00',\n    lastRestocked: 'Oct 10, 2024',\n  },\n  {\n    sku: 'SKU-5529',\n    productName: 'Laptop Stand Aluminum',\n    stockLevel: 178,\n    category: 'Accessories',\n    status: 'active',\n    unitPrice: '$45.99',\n    lastRestocked: 'Oct 17, 2024',\n  },\n  {\n    sku: 'SKU-4817',\n    productName: 'Mechanical Keyboard RGB',\n    stockLevel: 0,\n    category: 'Electronics',\n    status: 'discontinued',\n    unitPrice: '$129.99',\n    lastRestocked: 'Oct 05, 2024',\n  },\n];\n\nfunction getStatusBadge(status: Status) {\n  switch (status) {\n    case 'active':\n      return (\n        <Badge\n          className=\"border-0 bg-green-500/15 text-green-700 hover:bg-green-500/25 dark:bg-green-500/10 dark:text-green-400 dark:hover:bg-green-500/20\"\n          variant=\"outline\"\n        >\n          Active\n        </Badge>\n      );\n    case 'pending':\n      return (\n        <Badge\n          className=\"border-0 bg-amber-500/15 text-amber-700 hover:bg-amber-500/25 dark:bg-amber-500/10 dark:text-amber-300 dark:hover:bg-amber-500/20\"\n          variant=\"outline\"\n        >\n          Pending\n        </Badge>\n      );\n    case 'discontinued':\n      return (\n        <Badge\n          className=\"border-0 bg-rose-500/15 text-rose-700 hover:bg-rose-500/25 dark:bg-rose-500/10 dark:text-rose-400 dark:hover:bg-rose-500/20\"\n          variant=\"outline\"\n        >\n          Discontinued\n        </Badge>\n      );\n    case 'on-hold':\n      return (\n        <Badge\n          className=\"border-0 bg-blue-500/15 text-blue-700 hover:bg-blue-500/25 dark:bg-blue-500/10 dark:text-blue-400 dark:hover:bg-blue-500/20\"\n          variant=\"outline\"\n        >\n          On Hold\n        </Badge>\n      );\n    default:\n      return null;\n  }\n}\n\nexport default function Table03() {\n  const [selectedCategory, setSelectedCategory] = useState<string>('all');\n\n  const filteredData = data.filter((item) => {\n    const categoryMatch =\n      selectedCategory === 'all' || item.category === selectedCategory;\n    return categoryMatch;\n  });\n\n  const uniqueCategories = Array.from(\n    new Set(data.map((item) => item.category))\n  );\n\n  return (\n    <div className=\"space-y-6\">\n      <div className=\"flex flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n        <div>\n          <h3 className=\"text-balance font-semibold text-foreground text-lg\">\n            Product Inventory\n          </h3>\n          <p className=\"mt-1 text-pretty text-muted-foreground text-sm\">\n            Track and manage product stock levels across all warehouse\n            locations.\n          </p>\n        </div>\n        <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n          <Select\n            items={[\n              { value: 'all', label: 'All Categories' },\n              ...uniqueCategories.map((c) => ({ value: c, label: c })),\n            ]}\n            onValueChange={(value) => {\n              if (value) {\n                setSelectedCategory(value);\n              }\n            }}\n            value={selectedCategory}\n          >\n            <SelectTrigger className=\"w-full sm:w-[180px]\">\n              <SelectValue placeholder=\"Filter by category\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"all\">All Categories</SelectItem>\n              {uniqueCategories.map((category) => (\n                <SelectItem key={category} value={category}>\n                  {category}\n                </SelectItem>\n              ))}\n            </SelectContent>\n          </Select>\n        </div>\n      </div>\n\n      <div className=\"rounded-lg border bg-card\">\n        <Table>\n          <TableHeader>\n            <TableRow className=\"border-b hover:bg-transparent\">\n              <TableHead className=\"h-12 px-4 font-medium\">SKU</TableHead>\n              <TableHead className=\"h-12 px-4 font-medium\">\n                Product Name\n              </TableHead>\n              <TableHead className=\"h-12 px-4 font-medium\">Category</TableHead>\n              <TableHead className=\"h-12 px-4 font-medium\">Status</TableHead>\n              <TableHead className=\"h-12 px-4 text-right font-medium\">\n                Unit Price\n              </TableHead>\n              <TableHead className=\"h-12 px-4 text-right font-medium\">\n                Last Restocked\n              </TableHead>\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            {filteredData.length > 0 ? (\n              filteredData.map((item) => (\n                <TableRow className=\"hover:bg-muted/50\" key={item.sku}>\n                  <TableCell className=\"h-14 px-4 font-medium font-mono text-sm tabular-nums\">\n                    {item.sku}\n                  </TableCell>\n                  <TableCell className=\"h-14 px-4 font-medium\">\n                    {item.productName}\n                  </TableCell>\n                  <TableCell className=\"h-14 px-4 text-muted-foreground text-sm\">\n                    {item.category}\n                  </TableCell>\n                  <TableCell className=\"h-14 px-4\">\n                    {getStatusBadge(item.status)}\n                  </TableCell>\n                  <TableCell className=\"h-14 px-4 text-right font-mono font-semibold text-sm tabular-nums\">\n                    {item.unitPrice}\n                  </TableCell>\n                  <TableCell className=\"h-14 px-4 text-right text-muted-foreground text-sm\">\n                    {item.lastRestocked}\n                  </TableCell>\n                </TableRow>\n              ))\n            ) : (\n              <TableRow>\n                <TableCell\n                  className=\"h-24 text-center text-muted-foreground\"\n                  colSpan={6}\n                >\n                  No products found matching the selected filters.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "tables"
      ]
    },
    {
      "name": "table-04",
      "type": "registry:block",
      "title": "Table with Grouped Rows",
      "description": "A table with grouped rows block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "avatar",
        "badge",
        "table"
      ],
      "dependencies": [],
      "files": [
        {
          "path": "content/components/tables/table-04.tsx",
          "type": "registry:component",
          "target": "components/table-04.tsx",
          "content": "'use client';\n\nimport { Fragment } from 'react';\nimport { Avatar, AvatarFallback } from '@/components/ui/avatar';\nimport { Badge } from '@/components/ui/badge';\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '@/components/ui/table';\nimport { cn } from '@/lib/utils';\n\ninterface AssignedPerson {\n  name: string;\n  initials: string;\n}\n\ninterface TaskItem {\n  id: string;\n  task: string;\n  budget: string;\n  deadline: string;\n  assigned: AssignedPerson[];\n  status: string;\n}\n\ninterface TaskGroup {\n  name: string;\n  items: TaskItem[];\n}\n\ntype StatusVariant = 'success' | 'warning' | 'default' | 'secondary';\n\nconst avatarColors = [\n  'bg-blue-500',\n  'bg-purple-500',\n  'bg-emerald-500',\n  'bg-cyan-500',\n  'bg-rose-500',\n  'bg-indigo-500',\n];\n\nfunction getAvatarColor(initials: string) {\n  const seed = initials\n    .split('')\n    .reduce((acc, char) => acc + char.charCodeAt(0), 0);\n  return avatarColors[seed % avatarColors.length];\n}\n\nfunction AvatarStack({ people }: { people: AssignedPerson[] }) {\n  return (\n    <div className=\"-space-x-2 flex\">\n      {people.map((person, index) => (\n        <Avatar\n          className={cn(\n            'h-6 w-6 border-2 border-background text-[10px]',\n            getAvatarColor(person.initials)\n          )}\n          key={index}\n        >\n          <AvatarFallback\n            className={cn(\n              'font-medium text-white',\n              getAvatarColor(person.initials)\n            )}\n          >\n            {person.initials}\n          </AvatarFallback>\n        </Avatar>\n      ))}\n    </div>\n  );\n}\n\nconst statusStyles: Record<StatusVariant, { badge: string; dot: string }> = {\n  success: {\n    badge:\n      'bg-emerald-50 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300',\n    dot: 'bg-emerald-500',\n  },\n  warning: {\n    badge: 'bg-amber-50 text-amber-700 dark:bg-amber-950 dark:text-amber-300',\n    dot: 'bg-amber-500',\n  },\n  default: {\n    badge: 'bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300',\n    dot: 'bg-blue-500',\n  },\n  secondary: {\n    badge: 'bg-muted text-muted-foreground',\n    dot: 'bg-muted-foreground',\n  },\n};\n\nfunction StatusBadge({\n  status,\n  variant = 'default',\n}: {\n  status: string;\n  variant?: StatusVariant;\n}) {\n  const styles = statusStyles[variant];\n  return (\n    <Badge\n      className={cn('gap-1.5 rounded-full', styles.badge)}\n      variant=\"outline\"\n    >\n      <span\n        aria-hidden=\"true\"\n        className={cn('size-1.5 rounded-full', styles.dot)}\n      />\n      {status}\n    </Badge>\n  );\n}\n\nconst data: TaskGroup[] = [\n  {\n    name: 'Engineering',\n    items: [\n      {\n        id: '1',\n        task: 'API Integration Overhaul',\n        budget: '$32,000',\n        deadline: 'Dec 15, 2024',\n        assigned: [\n          { name: 'Marcus Chen', initials: 'MC' },\n          { name: 'Priya Sharma', initials: 'PS' },\n        ],\n        status: 'In Progress',\n      },\n      {\n        id: '2',\n        task: 'Database Migration',\n        budget: '$18,500',\n        deadline: 'Jan 20, 2025',\n        assigned: [{ name: 'David Kim', initials: 'DK' }],\n        status: 'Completed',\n      },\n      {\n        id: '3',\n        task: 'Mobile App Redesign',\n        budget: '$55,000',\n        deadline: 'Feb 28, 2025',\n        assigned: [\n          { name: 'Lena Rodriguez', initials: 'LR' },\n          { name: 'Tom Anderson', initials: 'TA' },\n          { name: 'Nina Patel', initials: 'NP' },\n        ],\n        status: 'Planning',\n      },\n    ],\n  },\n  {\n    name: 'Marketing',\n    items: [\n      {\n        id: '4',\n        task: 'Q1 Campaign Launch',\n        budget: '$24,000',\n        deadline: 'Jan 5, 2025',\n        assigned: [\n          { name: 'Rachel Green', initials: 'RG' },\n          { name: 'Chris Evans', initials: 'CE' },\n        ],\n        status: 'In Progress',\n      },\n      {\n        id: '5',\n        task: 'Brand Refresh Project',\n        budget: '$42,000',\n        deadline: 'Mar 1, 2025',\n        assigned: [{ name: 'Monica Geller', initials: 'MG' }],\n        status: 'Planning',\n      },\n    ],\n  },\n  {\n    name: 'Operations',\n    items: [\n      {\n        id: '6',\n        task: 'Vendor Contract Review',\n        budget: '$8,000',\n        deadline: 'Dec 30, 2024',\n        assigned: [\n          { name: 'Joe Tribbiani', initials: 'JT' },\n          { name: 'Phoebe Buffay', initials: 'PB' },\n        ],\n        status: 'Completed',\n      },\n      {\n        id: '7',\n        task: 'Office Expansion Setup',\n        budget: '$120,000',\n        deadline: 'Apr 15, 2025',\n        assigned: [{ name: 'Ross Geller', initials: 'RG' }],\n        status: 'On Hold',\n      },\n    ],\n  },\n];\n\nfunction getStatusVariant(status: string): StatusVariant {\n  switch (status) {\n    case 'Completed':\n      return 'success';\n    case 'In Progress':\n      return 'default';\n    case 'Planning':\n      return 'secondary';\n    case 'On Hold':\n      return 'warning';\n    default:\n      return 'default';\n  }\n}\n\nexport default function Table04() {\n  return (\n    <div className=\"rounded-lg border\">\n      <Table>\n        <TableHeader>\n          <TableRow>\n            <TableHead className=\"w-48 font-medium\">Task</TableHead>\n            <TableHead className=\"font-medium\">Budget</TableHead>\n            <TableHead className=\"font-medium\">Deadline</TableHead>\n            <TableHead className=\"font-medium\">Assigned</TableHead>\n            <TableHead className=\"w-28 font-medium\">Status</TableHead>\n          </TableRow>\n        </TableHeader>\n        <TableBody>\n          {data.map((group) => (\n            <Fragment key={group.name}>\n              <TableRow className=\"bg-muted/50 hover:bg-muted/50\">\n                <TableCell className=\"py-2 font-semibold\" colSpan={5}>\n                  {group.name}\n                  <span className=\"ml-2 font-normal text-muted-foreground\">\n                    {group.items.length}\n                  </span>\n                </TableCell>\n              </TableRow>\n              {group.items.map((item) => (\n                <TableRow key={item.id}>\n                  <TableCell className=\"font-medium\">{item.task}</TableCell>\n                  <TableCell>{item.budget}</TableCell>\n                  <TableCell>{item.deadline}</TableCell>\n                  <TableCell>\n                    <AvatarStack people={item.assigned} />\n                  </TableCell>\n                  <TableCell>\n                    <StatusBadge\n                      status={item.status}\n                      variant={getStatusVariant(item.status)}\n                    />\n                  </TableCell>\n                </TableRow>\n              ))}\n            </Fragment>\n          ))}\n        </TableBody>\n      </Table>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "tables"
      ]
    },
    {
      "name": "table-05",
      "type": "registry:block",
      "title": "Data Table",
      "description": "A data table block.",
      "author": "ephraim duncan <https://ephraimduncan.com>",
      "registryDependencies": [
        "badge",
        "button",
        "checkbox",
        "dropdown-menu",
        "input",
        "select",
        "table"
      ],
      "dependencies": [
        "@tanstack/react-table",
        "lucide-react"
      ],
      "files": [
        {
          "path": "content/components/tables/table-05.tsx",
          "type": "registry:component",
          "target": "components/table-05.tsx",
          "content": "'use client';\n\nimport {\n  type ColumnDef,\n  flexRender,\n  getCoreRowModel,\n  getFilteredRowModel,\n  getPaginationRowModel,\n  getSortedRowModel,\n  useReactTable,\n} from '@tanstack/react-table';\nimport {\n  ChevronLeft,\n  ChevronRight,\n  Eye,\n  MoreHorizontal,\n  Pencil,\n  Trash2,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Input } from '@/components/ui/input';\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select';\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '@/components/ui/table';\nimport { cn } from '@/lib/utils';\n\ntype Status = 'completed' | 'pending' | 'processing' | 'cancelled';\n\ninterface Item {\n  id: string;\n  name: string;\n  date: string;\n  status: Status;\n  amount: string;\n}\n\nconst statusConfig: Record<Status, { label: string; className: string }> = {\n  completed: {\n    label: 'Completed',\n    className:\n      'bg-emerald-500/15 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400',\n  },\n  pending: {\n    label: 'Pending',\n    className:\n      'bg-amber-500/15 text-amber-700 dark:bg-amber-500/10 dark:text-amber-400',\n  },\n  processing: {\n    label: 'Processing',\n    className:\n      'bg-blue-500/15 text-blue-700 dark:bg-blue-500/10 dark:text-blue-400',\n  },\n  cancelled: {\n    label: 'Cancelled',\n    className:\n      'bg-rose-500/15 text-rose-700 dark:bg-rose-500/10 dark:text-rose-400',\n  },\n};\n\nfunction StatusBadge({ status }: { status: Status }) {\n  const config = statusConfig[status];\n  return (\n    <Badge className={cn('border-0', config.className)} variant=\"outline\">\n      {config.label}\n    </Badge>\n  );\n}\n\nconst columns: ColumnDef<Item>[] = [\n  {\n    id: 'select',\n    header: ({ table }) => (\n      <Checkbox\n        aria-label=\"Select all\"\n        checked={table.getIsAllPageRowsSelected()}\n        indeterminate={\n          table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected()\n        }\n        onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}\n      />\n    ),\n    cell: ({ row }) => (\n      <Checkbox\n        aria-label=\"Select row\"\n        checked={row.getIsSelected()}\n        onCheckedChange={(value) => row.toggleSelected(!!value)}\n      />\n    ),\n    enableSorting: false,\n    enableHiding: false,\n  },\n  {\n    accessorKey: 'name',\n    header: 'Name',\n    cell: ({ row }) => (\n      <span className=\"font-medium\">{row.getValue('name')}</span>\n    ),\n  },\n  {\n    accessorKey: 'date',\n    header: 'Date',\n  },\n  {\n    accessorKey: 'status',\n    header: 'Status',\n    cell: ({ row }) => <StatusBadge status={row.getValue('status')} />,\n  },\n  {\n    accessorKey: 'amount',\n    header: () => <div className=\"text-right\">Amount</div>,\n    cell: ({ row }) => (\n      <div className=\"text-right font-medium\">{row.getValue('amount')}</div>\n    ),\n  },\n  {\n    id: 'actions',\n    cell: () => (\n      <div className=\"text-right\">\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={<Button className=\"h-8 w-8\" size=\"icon\" variant=\"ghost\" />}\n          >\n            <MoreHorizontal className=\"h-4 w-4\" />\n            <span className=\"sr-only\">Open menu</span>\n          </DropdownMenuTrigger>\n          <DropdownMenuContent align=\"end\">\n            <DropdownMenuItem>\n              <Eye className=\"mr-2 h-4 w-4\" />\n              View details\n            </DropdownMenuItem>\n            <DropdownMenuItem>\n              <Pencil className=\"mr-2 h-4 w-4\" />\n              Edit\n            </DropdownMenuItem>\n            <DropdownMenuSeparator />\n            <DropdownMenuItem className=\"text-destructive\">\n              <Trash2 className=\"mr-2 h-4 w-4\" />\n              Delete\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </div>\n    ),\n  },\n];\n\nconst data: Item[] = [\n  {\n    id: '1',\n    name: 'Project Alpha',\n    date: 'Jan 15, 2024',\n    status: 'completed',\n    amount: '$2,500',\n  },\n  {\n    id: '2',\n    name: 'Website Redesign',\n    date: 'Feb 3, 2024',\n    status: 'processing',\n    amount: '$4,200',\n  },\n  {\n    id: '3',\n    name: 'Mobile App MVP',\n    date: 'Feb 18, 2024',\n    status: 'pending',\n    amount: '$8,750',\n  },\n  {\n    id: '4',\n    name: 'Brand Identity',\n    date: 'Mar 5, 2024',\n    status: 'completed',\n    amount: '$1,800',\n  },\n  {\n    id: '5',\n    name: 'Marketing Campaign',\n    date: 'Mar 22, 2024',\n    status: 'cancelled',\n    amount: '$3,400',\n  },\n  {\n    id: '6',\n    name: 'Analytics Dashboard',\n    date: 'Apr 8, 2024',\n    status: 'processing',\n    amount: '$5,600',\n  },\n  {\n    id: '7',\n    name: 'E-commerce Platform',\n    date: 'Apr 25, 2024',\n    status: 'pending',\n    amount: '$12,000',\n  },\n  {\n    id: '8',\n    name: 'API Integration',\n    date: 'May 10, 2024',\n    status: 'completed',\n    amount: '$3,200',\n  },\n];\n\nexport default function Table05() {\n  const [globalFilter, setGlobalFilter] = useState('');\n\n  const table = useReactTable({\n    data,\n    columns,\n    getCoreRowModel: getCoreRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    getFilteredRowModel: getFilteredRowModel(),\n    onGlobalFilterChange: setGlobalFilter,\n    globalFilterFn: 'includesString',\n    state: {\n      globalFilter,\n    },\n    initialState: {\n      pagination: { pageSize: 5 },\n    },\n  });\n\n  const pageCount = table.getPageCount();\n  const currentPage = table.getState().pagination.pageIndex + 1;\n\n  return (\n    <div className=\"w-full max-w-3xl space-y-4\">\n      <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div className=\"flex items-center gap-2\">\n          <span className=\"text-muted-foreground text-sm\">Show</span>\n          <Select\n            onValueChange={(value) => table.setPageSize(Number(value))}\n            value={String(table.getState().pagination.pageSize)}\n          >\n            <SelectTrigger className=\"h-8 w-16\">\n              <SelectValue />\n            </SelectTrigger>\n            <SelectContent>\n              {[5, 10, 20].map((size) => (\n                <SelectItem key={size} value={String(size)}>\n                  {size}\n                </SelectItem>\n              ))}\n            </SelectContent>\n          </Select>\n          <span className=\"text-muted-foreground text-sm\">entries</span>\n        </div>\n        <Input\n          className=\"h-8 w-full sm:w-64\"\n          onChange={(e) => setGlobalFilter(e.target.value)}\n          placeholder=\"Search...\"\n          value={globalFilter}\n        />\n      </div>\n\n      <div className=\"rounded-lg border\">\n        <Table>\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id}>\n                {headerGroup.headers.map((header) => (\n                  <TableHead key={header.id}>\n                    {header.isPlaceholder\n                      ? null\n                      : flexRender(\n                          header.column.columnDef.header,\n                          header.getContext()\n                        )}\n                  </TableHead>\n                ))}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody>\n            {table.getRowModel().rows.length ? (\n              table.getRowModel().rows.map((row) => (\n                <TableRow\n                  data-state={row.getIsSelected() && 'selected'}\n                  key={row.id}\n                >\n                  {row.getVisibleCells().map((cell) => (\n                    <TableCell key={cell.id}>\n                      {flexRender(\n                        cell.column.columnDef.cell,\n                        cell.getContext()\n                      )}\n                    </TableCell>\n                  ))}\n                </TableRow>\n              ))\n            ) : (\n              <TableRow>\n                <TableCell\n                  className=\"h-24 text-center\"\n                  colSpan={columns.length}\n                >\n                  No results.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n\n      <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <p className=\"text-pretty text-muted-foreground text-sm\">\n          Showing{' '}\n          {table.getState().pagination.pageIndex *\n            table.getState().pagination.pageSize +\n            1}{' '}\n          to{' '}\n          {Math.min(\n            (table.getState().pagination.pageIndex + 1) *\n              table.getState().pagination.pageSize,\n            table.getFilteredRowModel().rows.length\n          )}{' '}\n          of {table.getFilteredRowModel().rows.length} entries\n        </p>\n        <div className=\"flex items-center gap-1\">\n          <Button\n            aria-label=\"Previous page\"\n            className=\"h-8 w-8\"\n            disabled={!table.getCanPreviousPage()}\n            onClick={() => table.previousPage()}\n            size=\"icon\"\n            variant=\"outline\"\n          >\n            <ChevronLeft className=\"h-4 w-4\" />\n            <span className=\"sr-only\">Previous page</span>\n          </Button>\n          {Array.from({ length: pageCount }, (_, i) => i + 1).map((page) => (\n            <Button\n              aria-label={`Go to page ${page}`}\n              className=\"h-8 w-8\"\n              key={page}\n              onClick={() => table.setPageIndex(page - 1)}\n              size=\"icon\"\n              variant={currentPage === page ? 'default' : 'outline'}\n            >\n              {page}\n            </Button>\n          ))}\n          <Button\n            aria-label=\"Next page\"\n            className=\"h-8 w-8\"\n            disabled={!table.getCanNextPage()}\n            onClick={() => table.nextPage()}\n            size=\"icon\"\n            variant=\"outline\"\n          >\n            <ChevronRight className=\"h-4 w-4\" />\n            <span className=\"sr-only\">Next page</span>\n          </Button>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
        }
      ],
      "categories": [
        "tables"
      ]
    }
  ]
}