{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "password-input",
  "title": "Password Input",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/password-input/PasswordInput.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { Eye, EyeOff } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { passwordInputVariants, type PasswordInputVariants } from './password-input.variants'\n\nexport interface PasswordInputProps\n  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'>,\n    PasswordInputVariants {\n  defaultValue?: string\n  showStrength?: boolean\n  showToggle?: boolean\n  minLength?: number\n  /** Wrapper className. Falls through to the bordered control, not the <input>. */\n  className?: string\n}\n\ninterface StrengthResult {\n  score: number\n  label: 'weak' | 'fair' | 'good' | 'strong'\n  color: string\n  barColor: string\n  percent: number\n}\n\nfunction computeStrength(pwd: string): StrengthResult {\n  if (!pwd) return { score: 0, label: 'weak', color: '', barColor: 'bg-transparent', percent: 0 }\n\n  let score = 0\n  if (pwd.length >= 6) score++\n  if (pwd.length >= 10) score++\n  if (/[A-Z]/.test(pwd) && /[a-z]/.test(pwd)) score++\n  if (/\\d/.test(pwd)) score++\n  if (/[^A-Za-z0-9]/.test(pwd)) score++\n\n  if (score <= 1) {\n    return { score, label: 'weak', color: 'text-destructive', barColor: 'bg-destructive', percent: 25 }\n  }\n  if (score <= 2) {\n    return {\n      score,\n      label: 'fair',\n      color: 'text-[var(--warning)]',\n      barColor: 'bg-[var(--warning)]',\n      percent: 50,\n    }\n  }\n  if (score <= 3) {\n    return {\n      score,\n      label: 'good',\n      color: 'text-[var(--info)]',\n      barColor: 'bg-[var(--info)]',\n      percent: 75,\n    }\n  }\n  return {\n    score,\n    label: 'strong',\n    color: 'text-[var(--success)]',\n    barColor: 'bg-[var(--success)]',\n    percent: 100,\n  }\n}\n\nconst PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(\n  (\n    {\n      value,\n      defaultValue,\n      placeholder = 'Enter password',\n      size = 'default',\n      variant = 'outlined',\n      disabled = false,\n      readOnly = false,\n      showStrength = false,\n      showToggle = true,\n      minLength = 0,\n      maxLength,\n      id,\n      name,\n      autoComplete = 'current-password',\n      className,\n      onChange,\n      onFocus,\n      onBlur,\n      ...rest\n    },\n    ref,\n  ) => {\n    const innerRef = React.useRef<HTMLInputElement | null>(null)\n    const setRefs = React.useCallback(\n      (node: HTMLInputElement | null) => {\n        innerRef.current = node\n        if (typeof ref === 'function') ref(node)\n        else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = node\n      },\n      [ref],\n    )\n\n    const isControlled = value !== undefined\n    const [internal, setInternal] = React.useState<string>(\n      defaultValue != null ? String(defaultValue) : '',\n    )\n    const currentValue = isControlled ? String(value ?? '') : internal\n\n    const [passwordVisible, setPasswordVisible] = React.useState(false)\n    const computedType = passwordVisible ? 'text' : 'password'\n\n    const strength = React.useMemo<StrengthResult>(() => computeStrength(currentValue), [currentValue])\n    const meetsMinLength = currentValue.length >= minLength\n\n    function toggleVisibility() {\n      if (disabled || readOnly) return\n      setPasswordVisible((v) => !v)\n      innerRef.current?.focus()\n    }\n\n    function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n      if (!isControlled) setInternal(e.target.value)\n      onChange?.(e)\n    }\n\n    const wrapperClasses = cn(\n      passwordInputVariants({ size, variant }),\n      'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]',\n      disabled && 'pointer-events-none opacity-50 cursor-not-allowed bg-muted/30',\n      className,\n    )\n\n    const inputPadding = size === 'sm' ? 'px-2.5' : size === 'lg' ? 'px-4' : 'px-3'\n    const togglePadding = size === 'sm' ? 'pr-2' : size === 'lg' ? 'pr-3' : 'pr-2.5'\n\n    return (\n      <div className=\"flex w-full flex-col gap-2\">\n        <div className={wrapperClasses} data-uipkge=\"\" data-slot=\"password-input\" data-size={size} data-variant={variant}>\n          <input\n            id={id}\n            ref={setRefs}\n            value={currentValue}\n            type={computedType}\n            disabled={disabled}\n            readOnly={readOnly}\n            maxLength={maxLength}\n            placeholder={placeholder}\n            name={name}\n            autoComplete={autoComplete}\n            onChange={handleChange}\n            onFocus={onFocus}\n            onBlur={onBlur}\n            className={cn('placeholder:text-muted-foreground w-full min-w-0 flex-1 bg-transparent outline-none', inputPadding)}\n            {...rest}\n          />\n          {showToggle && (\n            <div className={cn('flex shrink-0 items-center', togglePadding)}>\n              <button\n                type=\"button\"\n                aria-label={passwordVisible ? 'Hide password' : 'Show password'}\n                aria-pressed={passwordVisible}\n                disabled={disabled || readOnly}\n                onMouseDown={(e) => e.preventDefault()}\n                onClick={toggleVisibility}\n                className=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 shrink-0 rounded p-0.5 transition-colors focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed\"\n              >\n                {passwordVisible ? (\n                  <Eye className=\"size-4\" aria-hidden=\"true\" />\n                ) : (\n                  <EyeOff className=\"size-4\" aria-hidden=\"true\" />\n                )}\n              </button>\n            </div>\n          )}\n        </div>\n\n        {showStrength && currentValue && (\n          <div className=\"flex flex-col gap-1.5\">\n            <div className=\"bg-muted h-1.5 w-full overflow-hidden rounded-full\">\n              <div\n                className={cn('h-full rounded-full transition-all duration-300', strength.barColor)}\n                style={{ width: `${strength.percent}%` }}\n              />\n            </div>\n            <div className=\"flex items-center justify-between text-xs\">\n              <span className={cn('font-medium capitalize', strength.color)}>{strength.label}</span>\n              {minLength > 0 && (\n                <span className={meetsMinLength ? 'text-[var(--success)]' : 'text-muted-foreground'}>\n                  {currentValue.length} / {minLength} chars\n                </span>\n              )}\n            </div>\n          </div>\n        )}\n\n        {minLength > 0 && !showStrength && currentValue && (\n          <p className=\"text-muted-foreground text-xs\">Minimum {minLength} characters</p>\n        )}\n      </div>\n    )\n  },\n)\nPasswordInput.displayName = 'PasswordInput'\n\nexport { PasswordInput }\n",
      "type": "registry:ui",
      "target": "~/components/ui/password-input/PasswordInput.tsx"
    },
    {
      "path": "packages/registry-react/components/password-input/password-input.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\nexport const passwordInputVariants = cva(\n  'flex w-full items-center gap-1.5 overflow-hidden border transition-[color,box-shadow] outline-none rounded-md',\n  {\n    variants: {\n      size: {\n        sm: 'h-8 text-xs',\n        default: 'h-9 text-base md:text-sm',\n        lg: 'h-11 text-base',\n      },\n      variant: {\n        outlined: 'border-input bg-transparent shadow-xs',\n        filled: 'border-transparent bg-muted/50 shadow-none',\n        borderless: 'border-transparent bg-transparent shadow-none',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n      variant: 'outlined',\n    },\n  },\n)\n\nexport type PasswordInputVariants = VariantProps<typeof passwordInputVariants>\n",
      "type": "registry:ui",
      "target": "~/components/ui/password-input/password-input.variants.ts"
    },
    {
      "path": "packages/registry-react/components/password-input/index.ts",
      "content": "export { PasswordInput, type PasswordInputProps } from './PasswordInput'\nexport { passwordInputVariants, type PasswordInputVariants } from './password-input.variants'\n",
      "type": "registry:ui",
      "target": "~/components/ui/password-input/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Password input with show/hide toggle (Eye/EyeOff) and optional strength meter (weak/fair/good/strong with colored bar). Supports min length display, disabled, placeholder, and size variants.",
  "categories": [
    "control",
    "form"
  ]
}