{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "capabilities-badges",
  "type": "registry:component",
  "title": "Capabilities Badges",
  "description": "Surfaces what the configured storage adapter can do as a row of badges.",
  "dependencies": [
    "files-sdk",
    "lucide-react"
  ],
  "registryDependencies": [
    "badge"
  ],
  "files": [
    {
      "path": "registry/files-sdk/capabilities-badges/capabilities-badges.tsx",
      "type": "registry:component",
      "target": "components/files-sdk/capabilities-badges.tsx",
      "content": "\"use client\";\n\nimport type { AdapterCapabilities } from \"files-sdk\";\nimport type { UseFilesResult } from \"files-sdk/react\";\nimport { CheckIcon, Loader2Icon, XIcon } from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface CapabilitiesBadgesProps {\n  /** A `useFiles()` instance — capabilities are read through it. */\n  files: UseFilesResult;\n  /** Hide capabilities the adapter does not support instead of dimming them. */\n  supportedOnly?: boolean;\n  className?: string;\n}\n\n/** Render a seconds duration as a compact `7d` / `4h` / `30m` label. */\nconst formatDuration = (seconds: number): string => {\n  const units: [number, string][] = [\n    [86_400, \"d\"],\n    [3600, \"h\"],\n    [60, \"m\"],\n  ];\n  for (const [size, suffix] of units) {\n    if (seconds % size === 0 || seconds >= size) {\n      return `${Math.round(seconds / size)}${suffix}`;\n    }\n  }\n  return `${seconds}s`;\n};\n\nconst describe = (\n  caps: AdapterCapabilities\n): { label: string; supported: boolean }[] => [\n  { label: \"Folder listing\", supported: caps.delimiter },\n  {\n    label: caps.signedUrl.maxExpiresIn\n      ? `Signed URLs (max ${formatDuration(caps.signedUrl.maxExpiresIn)})`\n      : \"Signed URLs\",\n    supported: caps.signedUrl.supported,\n  },\n  { label: \"Range reads\", supported: caps.rangeRead },\n  { label: \"Multipart uploads\", supported: caps.multipart },\n  { label: \"Upload progress\", supported: caps.uploadProgress },\n  { label: \"Server-side copy\", supported: caps.serverSideCopy },\n  { label: \"Custom metadata\", supported: caps.metadata },\n  { label: \"Cache-Control\", supported: caps.cacheControl },\n];\n\n/**\n * Reads `capabilities()` and renders each adapter feature as a badge — a\n * supported check or an unsupported cross. Useful for surfacing what the\n * configured storage backend can do, and a building block for gating actions\n * (hide a \"share link\" button when `Signed URLs` is unsupported, etc.).\n */\nexport const CapabilitiesBadges = ({\n  files,\n  supportedOnly = false,\n  className,\n}: CapabilitiesBadgesProps) => {\n  const [caps, setCaps] = useState<AdapterCapabilities>();\n  const [isLoading, setIsLoading] = useState(true);\n\n  const filesRef = useRef(files);\n  filesRef.current = files;\n\n  useEffect(() => {\n    let cancelled = false;\n    const run = async () => {\n      setIsLoading(true);\n      try {\n        const result = await filesRef.current.capabilities();\n        if (!cancelled) {\n          setCaps(result);\n        }\n      } catch {\n        // The hook mirrors the error to `files.error` for display.\n      } finally {\n        if (!cancelled) {\n          setIsLoading(false);\n        }\n      }\n    };\n    void run();\n    return () => {\n      cancelled = true;\n    };\n  }, []);\n\n  if (isLoading && !caps) {\n    return (\n      <div\n        className={cn(\n          \"flex items-center gap-2 text-muted-foreground text-sm\",\n          className\n        )}\n      >\n        <Loader2Icon className=\"size-4 animate-spin\" /> Loading…\n      </div>\n    );\n  }\n\n  if (!caps) {\n    return null;\n  }\n\n  const rows = describe(caps).filter((row) => !supportedOnly || row.supported);\n\n  return (\n    <div className={cn(\"flex flex-wrap gap-1.5\", className)}>\n      {rows.map((row) => (\n        <Badge\n          key={row.label}\n          variant={row.supported ? \"secondary\" : \"outline\"}\n        >\n          {row.supported ? (\n            <CheckIcon className=\"text-emerald-600 dark:text-emerald-400\" />\n          ) : (\n            <XIcon className=\"text-muted-foreground\" />\n          )}\n          <span className={cn(!row.supported && \"text-muted-foreground\")}>\n            {row.label}\n          </span>\n        </Badge>\n      ))}\n    </div>\n  );\n};\n"
    }
  ]
}
