"use client" import { useState, useEffect } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Copy, Check, Eye, EyeOff, RefreshCw, Info, Key } from 'lucide-react' import { generatePassword, calculateEntropy, estimateTimeToCrack } from '@/utils/passwordGenerator' interface PasswordOptions { length: number includeUppercase: boolean includeLowercase: boolean includeNumbers: boolean includeSymbols: boolean excludeSimilar: boolean } export function PasswordGenerator() { const [password, setPassword] = useState('') const [showPassword, setShowPassword] = useState(true) const [copied, setCopied] = useState(false) const [options, setOptions] = useState({ length: 16, includeUppercase: true, includeLowercase: true, includeNumbers: true, includeSymbols: true, excludeSimilar: false, }) // Load settings from localStorage on mount useEffect(() => { const savedOptions = localStorage.getItem('passmaster-settings') if (savedOptions) { try { const parsed = JSON.parse(savedOptions) setOptions(prev => ({ ...prev, ...parsed })) } catch (error) { console.error('Failed to load saved settings:', error) } } }, []) // Save settings to localStorage when options change useEffect(() => { localStorage.setItem('passmaster-settings', JSON.stringify(options)) }, [options]) const handleGenerate = () => { const newPassword = generatePassword(options) setPassword(newPassword) setCopied(false) } const handleCopy = async () => { if (password) { try { await navigator.clipboard.writeText(password) setCopied(true) setTimeout(() => setCopied(false), 2000) } catch (error) { console.error('Failed to copy password:', error) // Fallback for older browsers const textArea = document.createElement('textarea') textArea.value = password document.body.appendChild(textArea) textArea.select() document.execCommand('copy') document.body.removeChild(textArea) setCopied(true) setTimeout(() => setCopied(false), 2000) } } } const getStrengthLevel = (entropy: number) => { if (entropy < 40) return { level: 'Weak', color: 'strength-weak', bg: 'bg-red-500' } if (entropy < 60) return { level: 'OK', color: 'strength-ok', bg: 'bg-yellow-500' } if (entropy < 80) return { level: 'Strong', color: 'strength-strong', bg: 'bg-blue-500' } return { level: 'Excellent', color: 'strength-excellent', bg: 'bg-green-500' } } const entropy = password ? calculateEntropy(password) : 0 const timeToCrack = password ? estimateTimeToCrack(password) : '' const strength = getStrengthLevel(entropy) return (
{/* Generated Password */}
{copied ? ( ) : ( )} {copied ? 'Copied!' : 'Copy'}
{/* Password Strength */} {password && (
Strength: {strength.level}
Entropy: {entropy.toFixed(1)} bits Time to crack: {timeToCrack}
)}
{/* Options */}
{/* Length Slider */}
setOptions({ ...options, length: parseInt(e.target.value) })} className="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer slider" />
8 128
{/* Character Options */}

Character Types

{[ { key: 'includeUppercase', label: 'Uppercase (A-Z)' }, { key: 'includeLowercase', label: 'Lowercase (a-z)' }, { key: 'includeNumbers', label: 'Numbers (0-9)' }, { key: 'includeSymbols', label: 'Symbols (!@#$%^&*)' }, ].map(({ key, label }) => ( ))}

Options

{/* Generate Button */} Generate Password {/* ARIA Live Region for Copy Feedback */}
{copied && 'Password copied to clipboard'}
) }