{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file-input-progress",
  "title": "Input-progress file",
  "description": "Styled file: the Input-progress set. 5 decorative file variations (BarFile, RingFile, PercentFile, MultiFile, QueueFile) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/file-input-progress.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\nimport { Check, File as FileIcon, Upload, X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Progress file-input family: 5 decorative pickers that show upload progress.\n   IMPORTANT: the progress here is a SIMULATED demo progress, not a real upload. No\n   network request is made; the progress increases by a fixed step at fixed\n   intervals (a fixed tick), which makes it fully deterministic (no Date.now or\n   Math.random). Every timer is cleared in the effect cleanup. Each export wraps a\n   real <input type=\"file\">; color comes ONLY from semantic tokens (transparency via\n   color-mix). The animations are disabled through useReducedMotion. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\ntype FileInputProps = {\n  className?: string\n  size?: StyledSize\n  multiple?: boolean\n  accept?: string\n  onFilesChange?: (files: File[]) => void\n}\n\nfunction fileKey(file: File): string {\n  return `${file.name}:${file.size}:${file.lastModified}`\n}\n\nfunction formatSize(bytes: number): string {\n  if (bytes < 1024) return `${bytes} B`\n  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`\n  return `${Math.round(bytes / (1024 * 1024))} MB`\n}\n\n/* Ortak dosya-state mantigi: secim ekle/kaldir + disari haber ver. */\nfunction useFiles(multiple: boolean | undefined, onFilesChange?: (files: File[]) => void) {\n  const [files, setFiles] = React.useState<File[]>([])\n\n  const add = React.useCallback(\n    (incoming: FileList | File[] | null) => {\n      if (!incoming) return\n      const list = Array.from(incoming)\n      if (list.length === 0) return\n      setFiles((prev) => {\n        if (!multiple) {\n          const next = [list[0]]\n          onFilesChange?.(next)\n          return next\n        }\n        const map = new Map(prev.map((f) => [fileKey(f), f]))\n        for (const f of list) map.set(fileKey(f), f)\n        const next = Array.from(map.values())\n        onFilesChange?.(next)\n        return next\n      })\n    },\n    [multiple, onFilesChange]\n  )\n\n  const remove = React.useCallback(\n    (key: string) => {\n      setFiles((prev) => {\n        const next = prev.filter((f) => fileKey(f) !== key)\n        onFilesChange?.(next)\n        return next\n      })\n    },\n    [onFilesChange]\n  )\n\n  return { files, add, remove }\n}\n\n/* Simulated progress clock. It increments the counter every TICK_MS and stops itself after MAX_TICKS; cleanup clears the interval in every case. */\nconst TICK_MS = 120\nconst STEP = 4\nconst STAGGER = 6\nconst MAX_TICKS = 80\n\nfunction useSimulatedTick(active: boolean, resetKey: string) {\n  const [tick, setTick] = React.useState(0)\n\n  React.useEffect(() => {\n    if (!active) {\n      setTick(0)\n      return\n    }\n    setTick(0)\n    let current = 0\n    const id = window.setInterval(() => {\n      current += 1\n      setTick(current)\n      if (current >= MAX_TICKS) window.clearInterval(id)\n    }, TICK_MS)\n    return () => window.clearInterval(id)\n  }, [active, resetKey])\n\n  return tick\n}\n\n/* index'e gore kaydirmali ilerleme: 0..100 arasi, deterministik. */\nfunction progressFor(tick: number, index: number): number {\n  const raw = (tick - index * STAGGER) * STEP\n  if (raw <= 0) return 0\n  return raw >= 100 ? 100 : raw\n}\n\nconst dropSurface: Record<StyledSize, string> = {\n  sm: \"gap-2 rounded-lg p-4 text-sm\",\n  md: \"gap-3 rounded-xl p-6 text-sm\",\n  lg: \"gap-3 rounded-2xl p-8 text-base\",\n  xl: \"gap-4 rounded-2xl p-10 text-base\",\n}\n\nconst iconSize: Record<StyledSize, string> = {\n  sm: \"size-5\",\n  md: \"size-6\",\n  lg: \"size-8\",\n  xl: \"size-9\",\n}\n\nconst barHeight: Record<StyledSize, string> = {\n  sm: \"h-1\",\n  md: \"h-1.5\",\n  lg: \"h-2\",\n  xl: \"h-2.5\",\n}\n\nconst clearBtn =\n  \"relative shrink-0 rounded-md text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 after:absolute after:-inset-1.5 motion-reduce:transition-none\"\n\nconst surfaceBase =\n  \"flex w-full flex-col items-center justify-center border border-dashed border-border bg-transparent text-center text-muted-foreground outline-none transition-colors duration-(--motion-base) ease-(--motion-ease) hover:border-primary/60 focus-visible:ring-[3px] focus-visible:ring-ring/50 data-[drag-active]:border-primary data-[drag-active]:bg-[color-mix(in_oklab,var(--color-primary)_10%,transparent)] data-[drag-active]:text-foreground motion-reduce:transition-none [&>svg]:shrink-0 [&>i]:leading-none\"\n\n/* 1) BarFile: a dropzone plus a horizontal progress bar for every selected file. */\nexport function BarFile({ className, size = \"md\", multiple = true, accept, onFilesChange }: FileInputProps) {\n  const reduce = useReducedMotion()\n  const id = React.useId()\n  const inputRef = React.useRef<HTMLInputElement>(null)\n  const [dragActive, setDragActive] = React.useState(false)\n  const { files, add, remove } = useFiles(multiple, onFilesChange)\n  const resetKey = files.map(fileKey).join(\"|\")\n  const tick = useSimulatedTick(files.length > 0, resetKey)\n\n  return (\n    <div data-slot=\"styled-file-input\" className={cn(\"w-full\", className)}>\n      <label htmlFor={id} className=\"sr-only\">\n        Choose a file to upload\n      </label>\n      <input\n        id={id}\n        ref={inputRef}\n        type=\"file\"\n        multiple={multiple}\n        accept={accept}\n        className=\"sr-only\"\n        onChange={(e) => add(e.currentTarget.files)}\n      />\n      <button\n        type=\"button\"\n        data-slot=\"styled-file-input-surface\"\n        data-drag-active={dragActive || undefined}\n        onClick={() => inputRef.current?.click()}\n        onDragOver={(e) => {\n          e.preventDefault()\n          setDragActive(true)\n        }}\n        onDragLeave={(e) => {\n          e.preventDefault()\n          setDragActive(false)\n        }}\n        onDrop={(e) => {\n          e.preventDefault()\n          setDragActive(false)\n          add(e.dataTransfer.files)\n        }}\n        className={cn(surfaceBase, dropSurface[size])}\n      >\n        <Upload className={cn(iconSize[size], \"text-primary\")} />\n        <span className=\"font-medium text-foreground\">Drop files to upload</span>\n        <span className=\"text-xs text-muted-foreground\">Progress shown below is a demo</span>\n      </button>\n\n      {files.length > 0 && (\n        <ul data-slot=\"styled-file-input-list\" className=\"mt-3 flex flex-col gap-2\">\n          {files.map((file, index) => {\n            const value = progressFor(tick, index)\n            const done = value >= 100\n            return (\n              <li\n                key={fileKey(file)}\n                className=\"flex flex-col gap-2 rounded-lg border border-border bg-surface-2 px-3 py-2 text-sm\"\n              >\n                <div className=\"flex items-center gap-2\">\n                  <FileIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n                  <span className=\"min-w-0 flex-1 truncate text-foreground\">{file.name}</span>\n                  <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\">\n                    {done ? formatSize(file.size) : `${value}%`}\n                  </span>\n                  <button\n                    type=\"button\"\n                    aria-label={`Remove ${file.name}`}\n                    onClick={() => remove(fileKey(file))}\n                    className={clearBtn}\n                  >\n                    <X className=\"size-4\" />\n                  </button>\n                </div>\n                <div\n                  role=\"progressbar\"\n                  aria-label={`Uploading ${file.name}`}\n                  aria-valuenow={value}\n                  aria-valuemin={0}\n                  aria-valuemax={100}\n                  className={cn(\n                    \"w-full overflow-hidden rounded-full bg-[color-mix(in_oklab,var(--color-foreground)_10%,transparent)]\",\n                    barHeight[size]\n                  )}\n                >\n                  <motion.div\n                    className={cn(\"h-full rounded-full\", done ? \"bg-success\" : \"bg-primary\")}\n                    initial={false}\n                    animate={{ width: `${value}%` }}\n                    transition={reduce ? { duration: 0 } : { duration: 0.12, ease: \"linear\" }}\n                  />\n                </div>\n              </li>\n            )\n          })}\n        </ul>\n      )}\n    </div>\n  )\n}\n\nconst ringSize: Record<StyledSize, string> = {\n  sm: \"size-12\",\n  md: \"size-16\",\n  lg: \"size-20\",\n  xl: \"size-24\",\n}\n\n/* 2) RingFile: dairesel ilerleme halkasi olan tek dosyalik yukleme hedefi. */\nexport function RingFile({ className, size = \"md\", accept, onFilesChange }: FileInputProps) {\n  const reduce = useReducedMotion()\n  const id = React.useId()\n  const inputRef = React.useRef<HTMLInputElement>(null)\n  const { files, add, remove } = useFiles(false, onFilesChange)\n  const resetKey = files.map(fileKey).join(\"|\")\n  const tick = useSimulatedTick(files.length > 0, resetKey)\n  const value = files.length > 0 ? progressFor(tick, 0) : 0\n  const done = value >= 100\n  const radius = 46\n  const circumference = 2 * Math.PI * radius\n\n  return (\n    <div data-slot=\"styled-file-input\" className={cn(\"flex items-center gap-4\", className)}>\n      <label htmlFor={id} className=\"sr-only\">\n        Choose a file to upload\n      </label>\n      <input\n        id={id}\n        ref={inputRef}\n        type=\"file\"\n        accept={accept}\n        className=\"sr-only\"\n        onChange={(e) => add(e.currentTarget.files)}\n      />\n      <button\n        type=\"button\"\n        data-slot=\"styled-file-input-surface\"\n        aria-label=\"Choose a file to upload\"\n        onClick={() => inputRef.current?.click()}\n        className={cn(\n          \"relative flex shrink-0 items-center justify-center rounded-full text-muted-foreground outline-none transition-colors duration-(--motion-base) ease-(--motion-ease) hover:text-primary focus-visible:ring-[3px] focus-visible:ring-ring/50 motion-reduce:transition-none\",\n          ringSize[size]\n        )}\n      >\n        <svg viewBox=\"0 0 100 100\" className=\"absolute inset-0 size-full -rotate-90\">\n          <circle\n            cx=\"50\"\n            cy=\"50\"\n            r={radius}\n            fill=\"none\"\n            strokeWidth=\"6\"\n            className=\"stroke-[color-mix(in_oklab,var(--color-foreground)_12%,transparent)]\"\n          />\n          <motion.circle\n            cx=\"50\"\n            cy=\"50\"\n            r={radius}\n            fill=\"none\"\n            strokeWidth=\"6\"\n            strokeLinecap=\"round\"\n            strokeDasharray={circumference}\n            className={done ? \"stroke-success\" : \"stroke-primary\"}\n            initial={false}\n            animate={{ strokeDashoffset: circumference * (1 - value / 100) }}\n            transition={reduce ? { duration: 0 } : { duration: 0.12, ease: \"linear\" }}\n          />\n        </svg>\n        {done ? (\n          <Check className=\"size-5 text-success\" />\n        ) : files.length > 0 ? (\n          <span className=\"text-xs font-medium tabular-nums text-foreground\">{value}%</span>\n        ) : (\n          <Upload className=\"size-5\" />\n        )}\n      </button>\n\n      {files.length > 0 ? (\n        <div className=\"flex min-w-0 items-center gap-2 text-sm\">\n          <span className=\"min-w-0 truncate text-foreground\">{files[0].name}</span>\n          <button\n            type=\"button\"\n            aria-label={`Remove ${files[0].name}`}\n            onClick={() => remove(fileKey(files[0]))}\n            className={clearBtn}\n          >\n            <X className=\"size-4\" />\n          </button>\n        </div>\n      ) : (\n        <span className=\"truncate text-sm text-muted-foreground\">No file chosen</span>\n      )}\n    </div>\n  )\n}\n\nconst rowSize: Record<StyledSize, string> = {\n  sm: \"gap-2 rounded-md p-2 text-sm\",\n  md: \"gap-3 rounded-lg p-3 text-sm\",\n  lg: \"gap-3 rounded-lg p-4 text-base\",\n  xl: \"gap-4 rounded-xl p-5 text-base\",\n}\n\n/* 3) PercentFile: a row that foregrounds the percentage number and fills behind it. */\nexport function PercentFile({ className, size = \"md\", accept, onFilesChange }: FileInputProps) {\n  const reduce = useReducedMotion()\n  const id = React.useId()\n  const inputRef = React.useRef<HTMLInputElement>(null)\n  const { files, add, remove } = useFiles(false, onFilesChange)\n  const resetKey = files.map(fileKey).join(\"|\")\n  const tick = useSimulatedTick(files.length > 0, resetKey)\n  const value = files.length > 0 ? progressFor(tick, 0) : 0\n  const done = value >= 100\n\n  return (\n    <div data-slot=\"styled-file-input\" className={cn(\"w-full\", className)}>\n      <label htmlFor={id} className=\"sr-only\">\n        Choose a file to upload\n      </label>\n      <input\n        id={id}\n        ref={inputRef}\n        type=\"file\"\n        accept={accept}\n        className=\"sr-only\"\n        onChange={(e) => add(e.currentTarget.files)}\n      />\n      {files.length === 0 ? (\n        <button\n          type=\"button\"\n          data-slot=\"styled-file-input-surface\"\n          onClick={() => inputRef.current?.click()}\n          className={cn(\n            \"flex w-full items-center justify-between border border-border bg-surface-2 text-left text-muted-foreground outline-none transition-colors duration-(--motion-base) ease-(--motion-ease) hover:border-primary/60 hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 motion-reduce:transition-none [&>svg]:size-4 [&>svg]:shrink-0 [&>i]:text-base [&>i]:leading-none\",\n            rowSize[size]\n          )}\n        >\n          <span className=\"font-medium text-foreground\">Choose a file</span>\n          <Upload />\n        </button>\n      ) : (\n        <div\n          data-slot=\"styled-file-input-surface\"\n          role=\"progressbar\"\n          aria-label={`Uploading ${files[0].name}`}\n          aria-valuenow={value}\n          aria-valuemin={0}\n          aria-valuemax={100}\n          className={cn(\n            \"relative flex w-full items-center overflow-hidden border border-border bg-surface-2\",\n            rowSize[size]\n          )}\n        >\n          <motion.span\n            aria-hidden=\"true\"\n            className={cn(\n              \"absolute inset-y-0 left-0\",\n              done\n                ? \"bg-[color-mix(in_oklab,var(--color-success)_16%,transparent)]\"\n                : \"bg-[color-mix(in_oklab,var(--color-primary)_16%,transparent)]\"\n            )}\n            initial={false}\n            animate={{ width: `${value}%` }}\n            transition={reduce ? { duration: 0 } : { duration: 0.12, ease: \"linear\" }}\n          />\n          <span className=\"relative z-10 flex min-w-0 flex-1 items-center gap-3\">\n            <span\n              className={cn(\n                \"shrink-0 text-lg font-semibold tabular-nums\",\n                done ? \"text-success\" : \"text-primary\"\n              )}\n            >\n              {value}%\n            </span>\n            <span className=\"min-w-0 flex-1 truncate text-foreground\">{files[0].name}</span>\n          </span>\n          <button\n            type=\"button\"\n            aria-label={`Remove ${files[0].name}`}\n            onClick={() => remove(fileKey(files[0]))}\n            className={cn(clearBtn, \"z-10\")}\n          >\n            <X className=\"size-4\" />\n          </button>\n        </div>\n      )}\n    </div>\n  )\n}\n\n/* 4) MultiFile: a dropzone that shows several files uploading at once with staggered progress. */\nexport function MultiFile({ className, size = \"md\", multiple = true, accept, onFilesChange }: FileInputProps) {\n  const reduce = useReducedMotion()\n  const id = React.useId()\n  const inputRef = React.useRef<HTMLInputElement>(null)\n  const [dragActive, setDragActive] = React.useState(false)\n  const { files, add, remove } = useFiles(multiple, onFilesChange)\n  const resetKey = files.map(fileKey).join(\"|\")\n  const tick = useSimulatedTick(files.length > 0, resetKey)\n  const total = files.length\n  const overall =\n    total === 0 ? 0 : Math.round(files.reduce((sum, _f, i) => sum + progressFor(tick, i), 0) / total)\n\n  return (\n    <div data-slot=\"styled-file-input\" className={cn(\"w-full\", className)}>\n      <label htmlFor={id} className=\"sr-only\">\n        Choose files to upload\n      </label>\n      <input\n        id={id}\n        ref={inputRef}\n        type=\"file\"\n        multiple={multiple}\n        accept={accept}\n        className=\"sr-only\"\n        onChange={(e) => add(e.currentTarget.files)}\n      />\n      <button\n        type=\"button\"\n        data-slot=\"styled-file-input-surface\"\n        data-drag-active={dragActive || undefined}\n        onClick={() => inputRef.current?.click()}\n        onDragOver={(e) => {\n          e.preventDefault()\n          setDragActive(true)\n        }}\n        onDragLeave={(e) => {\n          e.preventDefault()\n          setDragActive(false)\n        }}\n        onDrop={(e) => {\n          e.preventDefault()\n          setDragActive(false)\n          add(e.dataTransfer.files)\n        }}\n        className={cn(surfaceBase, dropSurface[size])}\n      >\n        <Upload className={cn(iconSize[size], \"text-primary\")} />\n        <span className=\"font-medium text-foreground\">Drop several files at once</span>\n        <span className=\"text-xs text-muted-foreground\">Each row keeps its own demo progress</span>\n      </button>\n\n      {total > 0 && (\n        <div className=\"mt-3 flex flex-col gap-3 rounded-xl border border-border bg-surface-2 p-3\">\n          <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n            <span>\n              {total} file{total > 1 ? \"s\" : \"\"}\n            </span>\n            <span className=\"tabular-nums\">{overall}% overall</span>\n          </div>\n          <ul data-slot=\"styled-file-input-list\" className=\"flex flex-col gap-2\">\n            {files.map((file, index) => {\n              const value = progressFor(tick, index)\n              const done = value >= 100\n              return (\n                <li key={fileKey(file)} className=\"flex items-center gap-2 text-sm\">\n                  <span\n                    className={cn(\n                      \"flex size-6 shrink-0 items-center justify-center rounded-md\",\n                      done\n                        ? \"bg-[color-mix(in_oklab,var(--color-success)_14%,transparent)] text-success\"\n                        : \"bg-[color-mix(in_oklab,var(--color-primary)_12%,transparent)] text-primary\"\n                    )}\n                  >\n                    {done ? <Check className=\"size-3.5\" /> : <FileIcon className=\"size-3.5\" />}\n                  </span>\n                  <span className=\"min-w-0 flex-1 truncate text-foreground\">{file.name}</span>\n                  <span\n                    role=\"progressbar\"\n                    aria-label={`Uploading ${file.name}`}\n                    aria-valuenow={value}\n                    aria-valuemin={0}\n                    aria-valuemax={100}\n                    className=\"h-1 w-20 shrink-0 overflow-hidden rounded-full bg-[color-mix(in_oklab,var(--color-foreground)_10%,transparent)]\"\n                  >\n                    <motion.span\n                      className={cn(\"block h-full rounded-full\", done ? \"bg-success\" : \"bg-primary\")}\n                      initial={false}\n                      animate={{ width: `${value}%` }}\n                      transition={reduce ? { duration: 0 } : { duration: 0.12, ease: \"linear\" }}\n                    />\n                  </span>\n                  <button\n                    type=\"button\"\n                    aria-label={`Remove ${file.name}`}\n                    onClick={() => remove(fileKey(file))}\n                    className={clearBtn}\n                  >\n                    <X className=\"size-4\" />\n                  </button>\n                </li>\n              )\n            })}\n          </ul>\n        </div>\n      )}\n    </div>\n  )\n}\n\n/* 5) QueueFile: an ordered queue view. Only the file whose turn it is shows as\n   \"uploading\", the ones before it as \"done\" and the ones after it as \"queued\". */\nexport function QueueFile({ className, size = \"md\", multiple = true, accept, onFilesChange }: FileInputProps) {\n  const reduce = useReducedMotion()\n  const id = React.useId()\n  const inputRef = React.useRef<HTMLInputElement>(null)\n  const { files, add, remove } = useFiles(multiple, onFilesChange)\n  const resetKey = files.map(fileKey).join(\"|\")\n  const tick = useSimulatedTick(files.length > 0, resetKey)\n\n  return (\n    <div data-slot=\"styled-file-input\" className={cn(\"w-full\", className)}>\n      <label htmlFor={id} className=\"sr-only\">\n        Choose files to queue\n      </label>\n      <input\n        id={id}\n        ref={inputRef}\n        type=\"file\"\n        multiple={multiple}\n        accept={accept}\n        className=\"sr-only\"\n        onChange={(e) => add(e.currentTarget.files)}\n      />\n      <button\n        type=\"button\"\n        data-slot=\"styled-file-input-surface\"\n        onClick={() => inputRef.current?.click()}\n        className={cn(\n          \"flex w-full items-center justify-center gap-2 border border-border bg-surface-2 font-medium text-foreground outline-none transition-colors duration-(--motion-base) ease-(--motion-ease) hover:border-primary/60 focus-visible:ring-[3px] focus-visible:ring-ring/50 motion-reduce:transition-none [&>svg]:size-4 [&>svg]:shrink-0 [&>i]:text-base [&>i]:leading-none\",\n          rowSize[size]\n        )}\n      >\n        <Upload />\n        Add to queue\n      </button>\n\n      {files.length > 0 && (\n        <ol data-slot=\"styled-file-input-list\" className=\"mt-3 flex flex-col gap-px overflow-hidden rounded-lg border border-border\">\n          {files.map((file, index) => {\n            const value = progressFor(tick, index)\n            const state = value >= 100 ? \"done\" : value > 0 ? \"uploading\" : \"queued\"\n            return (\n              <li\n                key={fileKey(file)}\n                data-state={state}\n                className=\"flex items-center gap-3 bg-surface-2 px-3 py-2 text-sm\"\n              >\n                <span className=\"w-5 shrink-0 text-center text-xs tabular-nums text-muted-foreground\">\n                  {index + 1}\n                </span>\n                <span className=\"min-w-0 flex-1 truncate text-foreground\">{file.name}</span>\n                <motion.span\n                  initial={false}\n                  animate={reduce ? undefined : { opacity: 1 }}\n                  className={cn(\n                    \"shrink-0 rounded-full px-2 py-0.5 text-xs font-medium tabular-nums\",\n                    state === \"done\" &&\n                      \"bg-[color-mix(in_oklab,var(--color-success)_14%,transparent)] text-success\",\n                    state === \"uploading\" &&\n                      \"bg-[color-mix(in_oklab,var(--color-primary)_14%,transparent)] text-primary\",\n                    state === \"queued\" && \"bg-surface-3 text-muted-foreground\"\n                  )}\n                >\n                  {state === \"done\" ? \"Done\" : state === \"uploading\" ? `${value}%` : \"Queued\"}\n                </motion.span>\n                <button\n                  type=\"button\"\n                  aria-label={`Remove ${file.name}`}\n                  onClick={() => remove(fileKey(file))}\n                  className={clearBtn}\n                >\n                  <X className=\"size-4\" />\n                </button>\n              </li>\n            )\n          })}\n        </ol>\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/file-input-progress.tsx"
    }
  ],
  "categories": [
    "styled",
    "file"
  ],
  "type": "registry:component"
}