{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "secret-field",
  "title": "Secret Field",
  "description": "A read-only masked value, such as an API key, with reveal and copy buttons.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "@drift-ui/use-controllable-state",
    "@drift-ui/use-copy-to-clipboard",
    "@drift-ui/shape"
  ],
  "files": [
    {
      "path": "registry/default/ui/secret-field.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from \"lucide-react\"\n\nimport { useControllableState } from \"@/registry/default/hooks/use-controllable-state\"\nimport { useCopyToClipboard } from \"@/registry/default/hooks/use-copy-to-clipboard\"\nimport {\n  getVisibleTail,\n  maskSecret,\n} from \"@/registry/default/lib/secret-field.ts\"\nimport { cn } from \"@/registry/default/lib/utils\"\n\nconst BUTTON_CLASS_NAME =\n  \"inline-flex size-7 shrink-0 items-center justify-center rounded-control text-muted-foreground outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-3.5\"\n\nfunction SecretField({\n  value,\n  visibleChars = 4,\n  revealed: revealedProp,\n  defaultRevealed = false,\n  onRevealedChange,\n  className,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  value: string\n  visibleChars?: number\n  revealed?: boolean\n  defaultRevealed?: boolean\n  onRevealedChange?: (revealed: boolean) => void\n}) {\n  const { value: revealed, setValue: setRevealed } = useControllableState({\n    value: revealedProp,\n    defaultValue: defaultRevealed,\n    onChange: onRevealedChange,\n  })\n  const { copied, copy } = useCopyToClipboard()\n  const tail = getVisibleTail(value, visibleChars)\n\n  return (\n    <div\n      role=\"group\"\n      data-slot=\"secret-field\"\n      data-revealed={revealed}\n      className={cn(\n        \"flex h-control w-full min-w-0 items-center gap-1 rounded-control border bg-transparent pr-1 pl-3 font-mono text-sm shadow-control dark:bg-input/30\",\n        className\n      )}\n      {...props}\n    >\n      <span data-slot=\"secret-field-value\" className=\"min-w-0 flex-1 truncate\">\n        {revealed ? (\n          value\n        ) : (\n          <>\n            <span aria-hidden>{maskSecret(value, visibleChars)}</span>\n            <span className=\"sr-only\">\n              {tail ? `Hidden, ends in ${tail}` : \"Hidden\"}\n            </span>\n          </>\n        )}\n      </span>\n      <button\n        type=\"button\"\n        data-slot=\"secret-field-reveal\"\n        aria-label=\"Reveal\"\n        aria-pressed={revealed}\n        onClick={() => setRevealed((previous) => !previous)}\n        className={BUTTON_CLASS_NAME}\n      >\n        {revealed ? <EyeOffIcon /> : <EyeIcon />}\n      </button>\n      <button\n        type=\"button\"\n        data-slot=\"secret-field-copy\"\n        aria-label=\"Copy\"\n        onClick={() => copy(value)}\n        className={BUTTON_CLASS_NAME}\n      >\n        {copied ? <CheckIcon /> : <CopyIcon />}\n      </button>\n      <span role=\"status\" className=\"sr-only\">\n        {copied ? \"Copied\" : \"\"}\n      </span>\n    </div>\n  )\n}\n\nexport { SecretField }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/default/lib/secret-field.ts",
      "content": "const MASK_CHAR = \"•\"\n// Fixed so the mask does not reveal how long the secret is.\nconst MASK_LENGTH = 8\n\nexport function getVisibleTail(value: string, visibleChars: number) {\n  const count = Math.floor(visibleChars)\n  // `!(count > 0)` also rejects NaN, which slice would treat as 0 and show the whole value.\n  if (!(count > 0) || value.length <= count) return \"\"\n  return value.slice(-count)\n}\n\nexport function maskSecret(value: string, visibleChars: number) {\n  return MASK_CHAR.repeat(MASK_LENGTH) + getVisibleTail(value, visibleChars)\n}\n",
      "type": "registry:lib"
    }
  ],
  "type": "registry:ui"
}