{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "password-field",
  "title": "Password Field",
  "description": "A native password input with controlled visibility, accessible reveal controls, and Caps Lock feedback.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/rivetui/components/password-field/password-field.tsx",
      "content": "\"use client\";\n\nimport { Eye, EyeOff } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype PasswordFieldProps = Omit<React.ComponentProps<\"input\">, \"className\" | \"type\"> & {\n  capsLockLabel?: string;\n  className?: string;\n  defaultVisible?: boolean;\n  hidePasswordLabel?: string;\n  inputClassName?: string;\n  onVisibleChange?: (visible: boolean) => void;\n  showCapsLockIndicator?: boolean;\n  showPasswordLabel?: string;\n  visible?: boolean;\n};\n\nconst PasswordField = React.forwardRef<HTMLInputElement, PasswordFieldProps>(function PasswordField(\n  {\n    capsLockLabel = \"Caps lock is on\",\n    className,\n    defaultVisible = false,\n    disabled,\n    hidePasswordLabel = \"Hide password\",\n    id,\n    inputClassName,\n    onBlur,\n    onFocus,\n    onVisibleChange,\n    showCapsLockIndicator = true,\n    showPasswordLabel = \"Show password\",\n    visible: visibleProp,\n    ...props\n  },\n  forwardedRef,\n) {\n  const generatedInputId = React.useId();\n  const capsLockId = React.useId();\n  const [uncontrolledVisible, setUncontrolledVisible] = React.useState(defaultVisible);\n  const [capsLockOn, setCapsLockOn] = React.useState(false);\n  const [inputFocused, setInputFocused] = React.useState(false);\n  const visible = visibleProp ?? uncontrolledVisible;\n  const inputId = id ?? generatedInputId;\n  const capsLockVisible = showCapsLockIndicator && inputFocused && capsLockOn && !disabled;\n\n  React.useEffect(() => {\n    if (!showCapsLockIndicator) {\n      setCapsLockOn(false);\n      return;\n    }\n\n    function updateCapsLock(event: KeyboardEvent | PointerEvent) {\n      setCapsLockOn(event.getModifierState(\"CapsLock\"));\n    }\n\n    window.addEventListener(\"keydown\", updateCapsLock, true);\n    window.addEventListener(\"keyup\", updateCapsLock, true);\n    window.addEventListener(\"pointerdown\", updateCapsLock, true);\n\n    return () => {\n      window.removeEventListener(\"keydown\", updateCapsLock, true);\n      window.removeEventListener(\"keyup\", updateCapsLock, true);\n      window.removeEventListener(\"pointerdown\", updateCapsLock, true);\n    };\n  }, [showCapsLockIndicator]);\n\n  function setVisible(nextVisible: boolean) {\n    if (visibleProp === undefined) {\n      setUncontrolledVisible(nextVisible);\n    }\n\n    onVisibleChange?.(nextVisible);\n  }\n\n  return (\n    <div\n      data-slot=\"password-field\"\n      data-visible={visible ? \"\" : undefined}\n      data-disabled={disabled ? \"\" : undefined}\n      className={cn(\"relative w-full\", className)}\n    >\n      <input\n        {...props}\n        ref={forwardedRef}\n        id={inputId}\n        type={visible ? \"text\" : \"password\"}\n        data-slot=\"password-field-input\"\n        disabled={disabled}\n        className={cn(\n          \"peer h-10 w-full min-w-0 rounded-none border border-input bg-background px-3 py-2 text-sm outline-none transition-[background-color,border-color,box-shadow,color] duration-100 placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground disabled:opacity-60 aria-invalid:border-destructive aria-invalid:focus-visible:border-destructive aria-invalid:focus-visible:ring-destructive/25 motion-reduce:transition-none\",\n          showCapsLockIndicator ? \"pr-20\" : \"pr-11\",\n          inputClassName,\n        )}\n        onFocus={(event) => {\n          onFocus?.(event);\n          setInputFocused(true);\n        }}\n        onBlur={(event) => {\n          onBlur?.(event);\n          setInputFocused(false);\n        }}\n      />\n\n      {showCapsLockIndicator ? (\n        <>\n          <span\n            data-slot=\"password-field-caps-lock\"\n            aria-hidden=\"true\"\n            data-visible={capsLockVisible ? \"\" : undefined}\n            className=\"pointer-events-none absolute top-1/2 right-11 -translate-y-1/2 font-mono text-[0.5625rem] uppercase tracking-[0.12em] text-muted-foreground opacity-0 transition-opacity duration-100 data-visible:opacity-100 motion-reduce:transition-none\"\n          >\n            Caps\n          </span>\n          <span id={capsLockId} className=\"sr-only\" aria-live=\"polite\">\n            {capsLockVisible ? capsLockLabel : \"\"}\n          </span>\n        </>\n      ) : null}\n\n      <button\n        type=\"button\"\n        data-slot=\"password-field-toggle\"\n        data-state={visible ? \"visible\" : \"hidden\"}\n        aria-controls={inputId}\n        aria-label={visible ? hidePasswordLabel : showPasswordLabel}\n        aria-pressed={visible}\n        disabled={disabled}\n        className=\"absolute top-px right-px inline-flex h-[calc(100%-2px)] w-[2.375rem] cursor-pointer items-center justify-center border-l border-input bg-background text-muted-foreground outline-none transition-[background-color,border-color,color,opacity] duration-100 hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-50 peer-aria-invalid:border-destructive motion-reduce:transition-none\"\n        onClick={() => setVisible(!visible)}\n      >\n        {visible ? (\n          <EyeOff className=\"size-4\" aria-hidden=\"true\" />\n        ) : (\n          <Eye className=\"size-4\" aria-hidden=\"true\" />\n        )}\n      </button>\n    </div>\n  );\n});\n\nexport type { PasswordFieldProps };\nexport { PasswordField };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}