Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Personal Vault - Secure Password Manager</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
.fade-in { | |
animation: fadeIn 0.5s ease-in-out; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(10px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.password-strength-meter { | |
height: 5px; | |
transition: all 0.3s ease; | |
} | |
.blur-effect { | |
filter: blur(5px); | |
transition: filter 0.3s ease; | |
} | |
.blur-effect:hover { | |
filter: blur(0); | |
} | |
</style> | |
</head> | |
<body class="bg-gray-900 text-gray-100 min-h-screen"> | |
<div class="container mx-auto px-4 py-8 max-w-4xl"> | |
<!-- Header --> | |
<header class="flex justify-between items-center mb-10"> | |
<div class="flex items-center"> | |
<i class="fas fa-lock-open text-indigo-500 text-3xl mr-3"></i> | |
<h1 class="text-2xl font-bold bg-gradient-to-r from-indigo-500 to-purple-600 bg-clip-text text-transparent"> | |
Personal Vault | |
</h1> | |
</div> | |
<div id="auth-section"> | |
<button id="logout-btn" class="hidden bg-gray-800 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition"> | |
<i class="fas fa-sign-out-alt mr-2"></i>Lock Vault | |
</button> | |
</div> | |
</header> | |
<!-- Login Screen --> | |
<div id="login-screen" class="fade-in max-w-md mx-auto bg-gray-800 rounded-xl shadow-2xl p-8"> | |
<div class="text-center mb-8"> | |
<i class="fas fa-lock text-indigo-500 text-5xl mb-4"></i> | |
<h2 class="text-2xl font-bold mb-2">Welcome to Your Vault</h2> | |
<p class="text-gray-400">Enter your master password to unlock</p> | |
</div> | |
<form id="login-form" class="space-y-6"> | |
<div> | |
<label for="master-password" class="block text-sm font-medium mb-2">Master Password</label> | |
<div class="relative"> | |
<input type="password" id="master-password" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Enter your master password" required> | |
<button type="button" class="absolute right-3 top-3 text-gray-400 hover:text-gray-200" id="toggle-password"> | |
<i class="fas fa-eye"></i> | |
</button> | |
</div> | |
<div class="mt-2 flex items-center"> | |
<input type="checkbox" id="remember-me" class="rounded bg-gray-700 border-gray-600 text-indigo-600 focus:ring-indigo-500"> | |
<label for="remember-me" class="ml-2 text-sm text-gray-400">Remember this device for 30 days</label> | |
</div> | |
</div> | |
<button type="submit" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-4 rounded-lg transition flex items-center justify-center"> | |
<i class="fas fa-unlock-alt mr-2"></i> Unlock Vault | |
</button> | |
</form> | |
<div class="mt-6 text-center text-sm text-gray-500"> | |
<p>First time here? Your vault will be created automatically.</p> | |
</div> | |
</div> | |
<!-- Main App (Hidden by default) --> | |
<div id="app-container" class="hidden fade-in"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-xl font-semibold">Your Saved Passwords</h2> | |
<button id="add-password-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center"> | |
<i class="fas fa-plus mr-2"></i> Add Password | |
</button> | |
</div> | |
<!-- Search and Filter --> | |
<div class="mb-6 flex items-center"> | |
<div class="relative flex-grow mr-4"> | |
<input type="text" id="search-passwords" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 pl-10 focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Search passwords..."> | |
<i class="fas fa-search absolute left-3 top-3 text-gray-500"></i> | |
</div> | |
<select id="category-filter" class="bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
<option value="all">All Categories</option> | |
<option value="social">Social Media</option> | |
<option value="work">Work</option> | |
<option value="finance">Finance</option> | |
<option value="shopping">Shopping</option> | |
<option value="other">Other</option> | |
</select> | |
</div> | |
<!-- Password List --> | |
<div id="password-list" class="space-y-3"> | |
<!-- Passwords will be loaded here --> | |
<div class="text-center py-10 text-gray-500"> | |
<i class="fas fa-lock-open text-4xl mb-3 opacity-30"></i> | |
<p>No passwords saved yet. Add your first password to get started.</p> | |
</div> | |
</div> | |
</div> | |
<!-- Add/Edit Password Modal --> | |
<div id="password-modal" class="hidden fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 p-4"> | |
<div class="bg-gray-800 rounded-xl shadow-2xl max-w-md w-full p-6 fade-in"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-lg font-semibold" id="modal-title">Add New Password</h3> | |
<button id="close-modal" class="text-gray-400 hover:text-white"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<form id="password-form" class="space-y-4"> | |
<input type="hidden" id="password-id"> | |
<div> | |
<label for="service-name" class="block text-sm font-medium mb-1">Service/Website</label> | |
<input type="text" id="service-name" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500" required> | |
</div> | |
<div> | |
<label for="username" class="block text-sm font-medium mb-1">Username/Email</label> | |
<input type="text" id="username" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500" required> | |
</div> | |
<div> | |
<label for="password" class="block text-sm font-medium mb-1">Password</label> | |
<div class="relative"> | |
<input type="password" id="password" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500" required> | |
<button type="button" class="absolute right-3 top-2 text-gray-400 hover:text-gray-200" id="toggle-modal-password"> | |
<i class="fas fa-eye"></i> | |
</button> | |
<button type="button" id="generate-password" class="absolute right-10 top-2 text-gray-400 hover:text-indigo-400" title="Generate strong password"> | |
<i class="fas fa-random"></i> | |
</button> | |
</div> | |
<div class="mt-2 flex items-center space-x-2"> | |
<div class="password-strength-meter flex-grow h-1 bg-gray-700 rounded overflow-hidden"> | |
<div id="strength-bar" class="h-full w-0 bg-red-500"></div> | |
</div> | |
<span id="strength-text" class="text-xs text-gray-400">Weak</span> | |
</div> | |
</div> | |
<div> | |
<label for="category" class="block text-sm font-medium mb-1">Category</label> | |
<select id="category" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
<option value="social">Social Media</option> | |
<option value="work">Work</option> | |
<option value="finance">Finance</option> | |
<option value="shopping">Shopping</option> | |
<option value="other">Other</option> | |
</select> | |
</div> | |
<div> | |
<label for="notes" class="block text-sm font-medium mb-1">Notes</label> | |
<textarea id="notes" rows="2" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea> | |
</div> | |
<div class="flex justify-end space-x-3 pt-4"> | |
<button type="button" id="cancel-password" class="px-4 py-2 rounded-lg border border-gray-600 hover:bg-gray-700 transition"> | |
Cancel | |
</button> | |
<button type="submit" class="px-4 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-700 transition"> | |
Save Password | |
</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<!-- Password Generator Modal --> | |
<div id="generator-modal" class="hidden fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 p-4"> | |
<div class="bg-gray-800 rounded-xl shadow-2xl max-w-md w-full p-6 fade-in"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-lg font-semibold">Password Generator</h3> | |
<button id="close-generator" class="text-gray-400 hover:text-white"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="space-y-4"> | |
<div class="flex items-center justify-between bg-gray-700 p-4 rounded-lg"> | |
<input type="text" id="generated-password" readonly class="bg-transparent border-none outline-none w-full text-lg font-mono"> | |
<button id="copy-generated" class="text-indigo-400 hover:text-indigo-300 ml-2"> | |
<i class="fas fa-copy"></i> | |
</button> | |
</div> | |
<div class="space-y-3"> | |
<div class="flex items-center justify-between"> | |
<label for="password-length" class="text-sm font-medium">Length: <span id="length-value">12</span></label> | |
<input type="range" id="password-length" min="8" max="32" value="12" class="w-32"> | |
</div> | |
<div class="space-y-2"> | |
<div class="flex items-center"> | |
<input type="checkbox" id="include-uppercase" checked class="rounded bg-gray-700 border-gray-600 text-indigo-600 focus:ring-indigo-500"> | |
<label for="include-uppercase" class="ml-2 text-sm">Include Uppercase (A-Z)</label> | |
</div> | |
<div class="flex items-center"> | |
<input type="checkbox" id="include-lowercase" checked class="rounded bg-gray-700 border-gray-600 text-indigo-600 focus:ring-indigo-500"> | |
<label for="include-lowercase" class="ml-2 text-sm">Include Lowercase (a-z)</label> | |
</div> | |
<div class="flex items-center"> | |
<input type="checkbox" id="include-numbers" checked class="rounded bg-gray-700 border-gray-600 text-indigo-600 focus:ring-indigo-500"> | |
<label for="include-numbers" class="ml-2 text-sm">Include Numbers (0-9)</label> | |
</div> | |
<div class="flex items-center"> | |
<input type="checkbox" id="include-symbols" checked class="rounded bg-gray-700 border-gray-600 text-indigo-600 focus:ring-indigo-500"> | |
<label for="include-symbols" class="ml-2 text-sm">Include Symbols (!@#$%^&*)</label> | |
</div> | |
</div> | |
</div> | |
<div class="flex justify-end pt-4"> | |
<button id="use-generated" class="px-4 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-700 transition"> | |
Use Password | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// DOM Elements | |
const loginScreen = document.getElementById('login-screen'); | |
const appContainer = document.getElementById('app-container'); | |
const loginForm = document.getElementById('login-form'); | |
const masterPasswordInput = document.getElementById('master-password'); | |
const togglePasswordBtn = document.getElementById('toggle-password'); | |
const logoutBtn = document.getElementById('logout-btn'); | |
const addPasswordBtn = document.getElementById('add-password-btn'); | |
const passwordList = document.getElementById('password-list'); | |
const passwordModal = document.getElementById('password-modal'); | |
const closeModalBtn = document.getElementById('close-modal'); | |
const cancelPasswordBtn = document.getElementById('cancel-password'); | |
const passwordForm = document.getElementById('password-form'); | |
const toggleModalPasswordBtn = document.getElementById('toggle-modal-password'); | |
const generatePasswordBtn = document.getElementById('generate-password'); | |
const generatorModal = document.getElementById('generator-modal'); | |
const closeGeneratorBtn = document.getElementById('close-generator'); | |
const useGeneratedBtn = document.getElementById('use-generated'); | |
const copyGeneratedBtn = document.getElementById('copy-generated'); | |
const searchPasswords = document.getElementById('search-passwords'); | |
const categoryFilter = document.getElementById('category-filter'); | |
// State | |
let masterKey = null; | |
let passwords = []; | |
let isEditing = false; | |
// Initialize | |
document.addEventListener('DOMContentLoaded', () => { | |
checkRememberMe(); | |
loadEventListeners(); | |
}); | |
// Event Listeners | |
function loadEventListeners() { | |
// Login | |
loginForm.addEventListener('submit', handleLogin); | |
togglePasswordBtn.addEventListener('click', () => togglePassword(masterPasswordInput, togglePasswordBtn)); | |
// Logout | |
logoutBtn.addEventListener('click', handleLogout); | |
// Password Modal | |
addPasswordBtn.addEventListener('click', () => showPasswordModal()); | |
closeModalBtn.addEventListener('click', () => hidePasswordModal()); | |
cancelPasswordBtn.addEventListener('click', () => hidePasswordModal()); | |
passwordForm.addEventListener('submit', handlePasswordSubmit); | |
toggleModalPasswordBtn.addEventListener('click', () => togglePassword(document.getElementById('password'), toggleModalPasswordBtn)); | |
// Password Generator | |
generatePasswordBtn.addEventListener('click', showGeneratorModal); | |
closeGeneratorBtn.addEventListener('click', () => generatorModal.classList.add('hidden')); | |
useGeneratedBtn.addEventListener('click', useGeneratedPassword); | |
copyGeneratedBtn.addEventListener('click', copyGeneratedPassword); | |
// Password strength | |
document.getElementById('password').addEventListener('input', updatePasswordStrength); | |
// Search and filter | |
searchPasswords.addEventListener('input', filterPasswords); | |
categoryFilter.addEventListener('change', filterPasswords); | |
// Generator options | |
document.getElementById('password-length').addEventListener('input', updateGenerator); | |
document.getElementById('include-uppercase').addEventListener('change', updateGenerator); | |
document.getElementById('include-lowercase').addEventListener('change', updateGenerator); | |
document.getElementById('include-numbers').addEventListener('change', updateGenerator); | |
document.getElementById('include-symbols').addEventListener('change', updateGenerator); | |
} | |
// Check if user is remembered | |
function checkRememberMe() { | |
const remembered = localStorage.getItem('rememberMe') === 'true'; | |
if (remembered) { | |
const storedHash = localStorage.getItem('masterPasswordHash'); | |
if (storedHash) { | |
masterPasswordInput.value = '••••••••••'; | |
document.getElementById('remember-me').checked = true; | |
} | |
} | |
} | |
// Handle Login | |
function handleLogin(e) { | |
e.preventDefault(); | |
const password = masterPasswordInput.value; | |
const rememberMe = document.getElementById('remember-me').checked; | |
if (password === '••••••••••' && localStorage.getItem('rememberMe') === 'true') { | |
// Use stored hash for remembered user | |
const storedHash = localStorage.getItem('masterPasswordHash'); | |
masterKey = storedHash; | |
unlockVault(); | |
} else { | |
// Create new master key | |
const salt = CryptoJS.lib.WordArray.random(128/8); | |
const key = CryptoJS.PBKDF2(password, salt, { | |
keySize: 256/32, | |
iterations: 1000 | |
}); | |
masterKey = key.toString(); | |
// Store hash if remember me is checked | |
if (rememberMe) { | |
localStorage.setItem('rememberMe', 'true'); | |
localStorage.setItem('masterPasswordHash', masterKey); | |
} else { | |
localStorage.removeItem('rememberMe'); | |
localStorage.removeItem('masterPasswordHash'); | |
} | |
unlockVault(); | |
} | |
} | |
// Unlock the vault | |
function unlockVault() { | |
// Load passwords from localStorage | |
const encryptedPasswords = localStorage.getItem('encryptedPasswords'); | |
if (encryptedPasswords) { | |
try { | |
// In a real app, you would decrypt here | |
// For this demo, we'll just store the encrypted data | |
passwords = JSON.parse(encryptedPasswords); | |
} catch (e) { | |
console.error('Failed to decrypt passwords', e); | |
passwords = []; | |
} | |
} | |
// Switch to app view | |
loginScreen.classList.add('hidden'); | |
appContainer.classList.remove('hidden'); | |
logoutBtn.classList.remove('hidden'); | |
// Render passwords | |
renderPasswords(); | |
} | |
// Handle Logout | |
function handleLogout() { | |
// Clear master key | |
masterKey = null; | |
// Switch to login view | |
loginScreen.classList.remove('hidden'); | |
appContainer.classList.add('hidden'); | |
logoutBtn.classList.add('hidden'); | |
// Clear password field | |
masterPasswordInput.value = ''; | |
document.getElementById('remember-me').checked = false; | |
} | |
// Show Password Modal | |
function showPasswordModal(editId = null) { | |
isEditing = editId !== null; | |
if (isEditing) { | |
document.getElementById('modal-title').textContent = 'Edit Password'; | |
const password = passwords.find(p => p.id === editId); | |
// In a real app, you would decrypt the password here | |
document.getElementById('password-id').value = password.id; | |
document.getElementById('service-name').value = password.service; | |
document.getElementById('username').value = password.username; | |
document.getElementById('password').value = password.password; | |
document.getElementById('category').value = password.category; | |
document.getElementById('notes').value = password.notes || ''; | |
updatePasswordStrength(); | |
} else { | |
document.getElementById('modal-title').textContent = 'Add New Password'; | |
passwordForm.reset(); | |
} | |
passwordModal.classList.remove('hidden'); | |
} | |
// Hide Password Modal | |
function hidePasswordModal() { | |
passwordModal.classList.add('hidden'); | |
} | |
// Handle Password Submit | |
function handlePasswordSubmit(e) { | |
e.preventDefault(); | |
const id = document.getElementById('password-id').value || generateId(); | |
const service = document.getElementById('service-name').value.trim(); | |
const username = document.getElementById('username').value.trim(); | |
const password = document.getElementById('password').value; | |
const category = document.getElementById('category').value; | |
const notes = document.getElementById('notes').value.trim(); | |
if (!service || !username || !password) { | |
alert('Please fill in all required fields'); | |
return; | |
} | |
// In a real app, you would encrypt the password here | |
const passwordData = { | |
id, | |
service, | |
username, | |
password, | |
category, | |
notes, | |
createdAt: new Date().toISOString(), | |
updatedAt: new Date().toISOString() | |
}; | |
if (isEditing) { | |
// Update existing password | |
const index = passwords.findIndex(p => p.id === id); | |
if (index !== -1) { | |
passwords[index] = passwordData; | |
} | |
} else { | |
// Add new password | |
passwords.push(passwordData); | |
} | |
// Save to localStorage | |
// In a real app, you would encrypt all passwords before saving | |
localStorage.setItem('encryptedPasswords', JSON.stringify(passwords)); | |
// Update UI | |
renderPasswords(); | |
hidePasswordModal(); | |
} | |
// Render Passwords | |
function renderPasswords() { | |
if (passwords.length === 0) { | |
passwordList.innerHTML = ` | |
<div class="text-center py-10 text-gray-500"> | |
<i class="fas fa-lock-open text-4xl mb-3 opacity-30"></i> | |
<p>No passwords saved yet. Add your first password to get started.</p> | |
</div> | |
`; | |
return; | |
} | |
passwordList.innerHTML = passwords.map(password => ` | |
<div class="password-item bg-gray-800 rounded-lg p-4 hover:bg-gray-750 transition border border-gray-750" data-id="${password.id}" data-category="${password.category}"> | |
<div class="flex justify-between items-start"> | |
<div class="flex items-center"> | |
<div class="w-10 h-10 rounded-full bg-indigo-900 flex items-center justify-center text-indigo-400 mr-3"> | |
${getServiceIcon(password.service)} | |
</div> | |
<div> | |
<h3 class="font-medium">${password.service}</h3> | |
<p class="text-sm text-gray-400">${password.username}</p> | |
</div> | |
</div> | |
<div class="flex space-x-2"> | |
<button class="copy-password text-gray-400 hover:text-indigo-400" data-password="${password.password}" title="Copy password"> | |
<i class="fas fa-copy"></i> | |
</button> | |
<button class="edit-password text-gray-400 hover:text-yellow-400" data-id="${password.id}" title="Edit"> | |
<i class="fas fa-edit"></i> | |
</button> | |
<button class="delete-password text-gray-400 hover:text-red-400" data-id="${password.id}" title="Delete"> | |
<i class="fas fa-trash-alt"></i> | |
</button> | |
</div> | |
</div> | |
<div class="mt-3 flex justify-between items-center text-xs"> | |
<span class="category-tag px-2 py-1 rounded-full ${getCategoryColor(password.category)}"> | |
${getCategoryName(password.category)} | |
</span> | |
<span class="text-gray-500">Last updated: ${formatDate(password.updatedAt)}</span> | |
</div> | |
</div> | |
`).join(''); | |
// Add event listeners to new elements | |
document.querySelectorAll('.copy-password').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const password = e.currentTarget.getAttribute('data-password'); | |
copyToClipboard(password); | |
showToast('Password copied to clipboard!', 'success'); | |
}); | |
}); | |
document.querySelectorAll('.edit-password').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const id = e.currentTarget.getAttribute('data-id'); | |
showPasswordModal(id); | |
}); | |
}); | |
document.querySelectorAll('.delete-password').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const id = e.currentTarget.getAttribute('data-id'); | |
if (confirm('Are you sure you want to delete this password?')) { | |
deletePassword(id); | |
} | |
}); | |
}); | |
} | |
// Delete Password | |
function deletePassword(id) { | |
passwords = passwords.filter(p => p.id !== id); | |
localStorage.setItem('encryptedPasswords', JSON.stringify(passwords)); | |
renderPasswords(); | |
showToast('Password deleted', 'info'); | |
} | |
// Filter Passwords | |
function filterPasswords() { | |
const searchTerm = searchPasswords.value.toLowerCase(); | |
const category = categoryFilter.value; | |
document.querySelectorAll('.password-item').forEach(item => { | |
const matchesSearch = item.textContent.toLowerCase().includes(searchTerm); | |
const matchesCategory = category === 'all' || item.getAttribute('data-category') === category; | |
if (matchesSearch && matchesCategory) { | |
item.classList.remove('hidden'); | |
} else { | |
item.classList.add('hidden'); | |
} | |
}); | |
} | |
// Password Generator | |
function showGeneratorModal() { | |
updateGenerator(); | |
generatorModal.classList.remove('hidden'); | |
} | |
function updateGenerator() { | |
const length = parseInt(document.getElementById('password-length').value); | |
const includeUpper = document.getElementById('include-uppercase').checked; | |
const includeLower = document.getElementById('include-lowercase').checked; | |
const includeNumbers = document.getElementById('include-numbers').checked; | |
const includeSymbols = document.getElementById('include-symbols').checked; | |
document.getElementById('length-value').textContent = length; | |
const chars = []; | |
if (includeUpper) chars.push('ABCDEFGHIJKLMNOPQRSTUVWXYZ'); | |
if (includeLower) chars.push('abcdefghijklmnopqrstuvwxyz'); | |
if (includeNumbers) chars.push('0123456789'); | |
if (includeSymbols) chars.push('!@#$%^&*()_+-=[]{}|;:,.<>?'); | |
if (chars.length === 0) { | |
document.getElementById('generated-password').value = 'Select at least one character type'; | |
return; | |
} | |
let password = ''; | |
const allChars = chars.join(''); | |
// Ensure at least one character from each selected set | |
chars.forEach(charSet => { | |
password += charSet.charAt(Math.floor(Math.random() * charSet.length)); | |
}); | |
// Fill the rest randomly | |
for (let i = password.length; i < length; i++) { | |
password += allChars.charAt(Math.floor(Math.random() * allChars.length)); | |
} | |
// Shuffle the password to mix the required characters | |
password = password.split('').sort(() => 0.5 - Math.random()).join(''); | |
document.getElementById('generated-password').value = password; | |
} | |
function useGeneratedPassword() { | |
const generatedPassword = document.getElementById('generated-password').value; | |
document.getElementById('password').value = generatedPassword; | |
updatePasswordStrength(); | |
generatorModal.classList.add('hidden'); | |
} | |
function copyGeneratedPassword() { | |
const generatedPassword = document.getElementById('generated-password').value; | |
copyToClipboard(generatedPassword); | |
showToast('Password copied to clipboard!', 'success'); | |
} | |
// Password Strength | |
function updatePasswordStrength() { | |
const password = document.getElementById('password').value; | |
const strengthBar = document.getElementById('strength-bar'); | |
const strengthText = document.getElementById('strength-text'); | |
if (!password) { | |
strengthBar.style.width = '0%'; | |
strengthText.textContent = ''; | |
return; | |
} | |
// Simple strength calculation | |
let strength = 0; | |
// Length | |
if (password.length >= 8) strength += 1; | |
if (password.length >= 12) strength += 1; | |
if (password.length >= 16) strength += 1; | |
// Character variety | |
if (/[A-Z]/.test(password)) strength += 1; | |
if (/[a-z]/.test(password)) strength += 1; | |
if (/[0-9]/.test(password)) strength += 1; | |
if (/[^A-Za-z0-9]/.test(password)) strength += 1; | |
// Common patterns (negative) | |
if (password.length < 6) strength = 0; | |
if (password === password.toLowerCase()) strength = Math.max(0, strength - 1); | |
if (password === password.toUpperCase()) strength = Math.max(0, strength - 1); | |
if (/^[0-9]+$/.test(password)) strength = Math.max(0, strength - 2); | |
// Map to 0-100% for the bar | |
const percentage = Math.min(100, strength * 20); | |
strengthBar.style.width = `${percentage}%`; | |
// Update color and text | |
if (percentage < 30) { | |
strengthBar.style.backgroundColor = '#ef4444'; // red | |
strengthText.textContent = 'Weak'; | |
} else if (percentage < 70) { | |
strengthBar.style.backgroundColor = '#f59e0b'; // yellow | |
strengthText.textContent = 'Medium'; | |
} else { | |
strengthBar.style.backgroundColor = '#10b981'; // green | |
strengthText.textContent = 'Strong'; | |
} | |
} | |
// Helper Functions | |
function togglePassword(input, button) { | |
const type = input.getAttribute('type') === 'password' ? 'text' : 'password'; | |
input.setAttribute('type', type); | |
if (type === 'text') { | |
button.innerHTML = '<i class="fas fa-eye-slash"></i>'; | |
} else { | |
button.innerHTML = '<i class="fas fa-eye"></i>'; | |
} | |
} | |
function generateId() { | |
return Date.now().toString(36) + Math.random().toString(36).substr(2); | |
} | |
function copyToClipboard(text) { | |
const textarea = document.createElement('textarea'); | |
textarea.value = text; | |
document.body.appendChild(textarea); | |
textarea.select(); | |
document.execCommand('copy'); | |
document.body.removeChild(textarea); | |
} | |
function showToast(message, type = 'info') { | |
const toast = document.createElement('div'); | |
toast.className = `fixed bottom-4 right-4 px-4 py-2 rounded-lg shadow-lg bg-gray-800 border-l-4 ${type === 'success' ? 'border-green-500' : type === 'error' ? 'border-red-500' : 'border-blue-500'} text-white animate-bounce`; | |
toast.textContent = message; | |
document.body.appendChild(toast); | |
setTimeout(() => { | |
toast.classList.remove('animate-bounce'); | |
toast.classList.add('opacity-0', 'transition-opacity', 'duration-300'); | |
setTimeout(() => toast.remove(), 300); | |
}, 3000); | |
} | |
function getServiceIcon(service) { | |
const serviceLower = service.toLowerCase(); | |
if (serviceLower.includes('google')) return '<i class="fab fa-google"></i>'; | |
if (serviceLower.includes('facebook')) return '<i class="fab fa-facebook-f"></i>'; | |
if (serviceLower.includes('twitter')) return '<i class="fab fa-twitter"></i>'; | |
if (serviceLower.includes('instagram')) return '<i class="fab fa-instagram"></i>'; | |
if (serviceLower.includes('linkedin')) return '<i class="fab fa-linkedin-in"></i>'; | |
if (serviceLower.includes('github')) return '<i class="fab fa-github"></i>'; | |
if (serviceLower.includes('amazon')) return '<i class="fab fa-amazon"></i>'; | |
if (serviceLower.includes('apple')) return '<i class="fab fa-apple"></i>'; | |
if (serviceLower.includes('microsoft')) return '<i class="fab fa-microsoft"></i>'; | |
if (serviceLower.includes('paypal')) return '<i class="fab fa-paypal"></i>'; | |
if (serviceLower.includes('bank') || serviceLower.includes('finance')) return '<i class="fas fa-university"></i>'; | |
if (serviceLower.includes('email') || serviceLower.includes('mail')) return '<i class="fas fa-envelope"></i>'; | |
return '<i class="fas fa-globe"></i>'; | |
} | |
function getCategoryName(category) { | |
const names = { | |
'social': 'Social Media', | |
'work': 'Work', | |
'finance': 'Finance', | |
'shopping': 'Shopping', | |
'other': 'Other' | |
}; | |
return names[category] || category; | |
} | |
function getCategoryColor(category) { | |
const colors = { | |
'social': 'bg-blue-900 text-blue-300', | |
'work': 'bg-purple-900 text-purple-300', | |
'finance': 'bg-green-900 text-green-300', | |
'shopping': 'bg-yellow-900 text-yellow-300', | |
'other': 'bg-gray-700 text-gray-300' | |
}; | |
return colors[category] || 'bg-gray-700 text-gray-300'; | |
} | |
function formatDate(dateString) { | |
const date = new Date(dateString); | |
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); | |
} | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MonsieurMory/password-manager" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |