anycoder-1f4bcb1f / index.html
Superwolff's picture
Upload folder using huggingface_hub
1e92f15 verified
Raw
History Blame Contribute Delete
24.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secure Password Manager</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
<style>
:root {
--primary-color: #6366f1;
--primary-hover: #4f46e5;
--bg-color: #0f172a;
--card-bg: #1e293b;
--text-main: #f1f5f9;
--text-muted: #94a3b8;
--success: #10b981;
--danger: #ef4444;
--border-radius: 12px;
--transition: all 0.3s ease;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg-color);
color: var(--text-main);
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
header {
background-color: var(--card-bg);
padding: 1rem 2rem;
border-bottom: 1px solid #334155;
display: flex;
justify-content: space-between;
align-items: center;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-size: 1.5rem;
font-weight: 700;
color: var(--text-main);
}
.brand i {
color: var(--primary-color);
}
.brand span {
color: var(--primary-color);
font-weight: 300;
}
.anycoder-link {
font-size: 0.85rem;
color: var(--text-muted);
text-decoration: none;
border: 1px solid var(--text-muted);
padding: 5px 10px;
border-radius: 6px;
transition: var(--transition);
}
.anycoder-link:hover {
border-color: var(--primary-color);
color: var(--primary-color);
box-shadow: 0 0 10px rgba(99, 102, 241, 0.3);
}
/* Main Layout */
main {
flex: 1;
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
h1 {
font-size: 2rem;
font-weight: 600;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
border: none;
padding: 10px 20px;
border-radius: var(--border-radius);
cursor: pointer;
font-size: 1rem;
display: flex;
align-items: center;
gap: 10px;
transition: var(--transition);
}
.btn-primary:hover {
background-color: var(--primary-hover);
transform: translateY(-1px);
}
/* Stats Grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background-color: var(--card-bg);
padding: 1.5rem;
border-radius: var(--border-radius);
border: 1px solid #334155;
}
.stat-label {
color: var(--text-muted);
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.stat-value {
font-size: 2.5rem;
font-weight: 700;
color: var(--text-main);
}
/* Password Grid */
.password-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
}
.password-card {
background-color: var(--card-bg);
border-radius: var(--border-radius);
padding: 1.5rem;
border: 1px solid #334155;
transition: var(--transition);
position: relative;
overflow: hidden;
}
.password-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.3);
border-color: var(--primary-color);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.site-name {
font-size: 1.25rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
}
.site-icon {
width: 32px;
height: 32px;
background-color: rgba(99, 102, 241, 0.2);
color: var(--primary-color);
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
}
.card-actions {
display: flex;
gap: 10px;
}
.btn-icon {
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
padding: 5px;
border-radius: 6px;
transition: var(--transition);
font-size: 0.9rem;
}
.btn-icon:hover {
color: var(--primary-color);
background-color: rgba(99, 102, 241, 0.1);
}
.btn-delete:hover {
color: var(--danger);
background-color: rgba(239, 68, 68, 0.1);
}
.credential-row {
margin-bottom: 1rem;
}
.label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--text-muted);
margin-bottom: 4px;
display: block;
}
.input-group {
display: flex;
gap: 10px;
}
input[type="text"], input[type="password"] {
flex: 1;
background-color: #0f172a;
border: 1px solid #334155;
color: var(--text-main);
padding: 10px;
border-radius: 6px;
font-family: inherit;
}
input:focus {
outline: none;
border-color: var(--primary-color);
}
.toggle-password {
background-color: #334155;
border: none;
color: var(--text-muted);
padding: 0 12px;
border-radius: 6px;
cursor: pointer;
transition: var(--transition);
}
.toggle-password:hover {
color: var(--text-main);
background-color: #475569;
}
.btn-copy {
background-color: var(--primary-color);
color: white;
border: none;
padding: 0 15px;
border-radius: 6px;
cursor: pointer;
font-size: 0.85rem;
transition: var(--transition);
}
.btn-copy:hover {
background-color: var(--primary-hover);
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(4px);
z-index: 100;
justify-content: center;
align-items: center;
animation: fadeIn 0.3s ease;
}
.modal-content {
background-color: var(--card-bg);
padding: 2rem;
border-radius: var(--border-radius);
width: 90%;
max-width: 500px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5);
animation: slideUp 0.3s ease;
}
.modal-header {
display: flex;
justify-content: space-between;
margin-bottom: 2rem;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
color: var(--text-muted);
cursor: pointer;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: var(--text-muted);
}
.form-group input {
width: 100%;
padding: 10px;
border-radius: 6px;
border: 1px solid #334155;
background-color: #0f172a;
color: white;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 1rem;
margin-top: 2rem;
}
.btn-secondary {
background-color: transparent;
border: 1px solid #334155;
color: var(--text-main);
padding: 10px 20px;
border-radius: var(--border-radius);
cursor: pointer;
}
.btn-secondary:hover {
border-color: var(--text-muted);
}
/* Toast Notification */
.toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background-color: var(--success);
color: white;
padding: 1rem 2rem;
border-radius: var(--border-radius);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
gap: 10px;
transform: translateY(100px);
transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
z-index: 200;
}
.toast.show {
transform: translateY(0);
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
/* Responsive */
@media (max-width: 768px) {
.password-grid {
grid-template-columns: 1fr;
}
header {
flex-direction: column;
gap: 1rem;
text-align: center;
}
}
</style>
</head>
<body>
<header>
<div class="brand">
<i class="fa-solid fa-lock"></i>
Secure<span>Vault</span>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
Built with anycoder
</a>
</header>
<main>
<div class="dashboard-header">
<h1>My Credentials</h1>
<button class="btn-primary" onclick="openModal()">
<i class="fa-solid fa-plus"></i> Add Account
</button>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Total Passwords</div>
<div class="stat-value" id="total-count">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Weak Passwords</div>
<div class="stat-value" id="weak-count" style="color: var(--danger)">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Last Updated</div>
<div class="stat-value" id="last-updated">-</div>
</div>
</div>
<div class="password-grid" id="password-container">
<!-- Password cards will be injected here via JS -->
</div>
<div style="text-align: center; margin-top: 4rem; color: var(--text-muted);">
<p><i class="fa-solid fa-circle-check"></i> All data is stored locally in your browser. No data leaves your device.</p>
</div>
</main>
<!-- Add Account Modal -->
<div class="modal" id="addModal">
<div class="modal-content">
<div class="modal-header">
<h2>Add New Credential</h2>
<button class="close-btn" onclick="closeModal()">&times;</button>
</div>
<form id="addForm">
<div class="form-group">
<label for="siteName">Website / Service Name</label>
<input type="text" id="siteName" placeholder="e.g. Gmail, iCloud, Netflix" required>
</div>
<div class="form-group">
<label for="username">Username / Email</label>
<input type="text" id="username" placeholder="e.g. user@example.com" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<div class="input-group">
<input type="password" id="password" placeholder="Enter password" required>
<button type="button" class="toggle-password" onclick="togglePasswordVisibility('password')">
<i class="fa-solid fa-eye"></i>
</button>
</div>
</div>
<div class="form-group">
<label for="category">Category</label>
<select id="category" style="width: 100%; padding: 10px; border-radius: 6px; border: 1px solid #334155; background-color: #0f172a; color: white;">
<option value="email">Email</option>
<option value="social">Social Media</option>
<option value="finance">Finance</option>
<option value="other">Other</option>
</select>
</div>
<div class="modal-footer">
<button type="button" class="btn-secondary" onclick="closeModal()">Cancel</button>
<button type="submit" class="btn-primary">Save Credential</button>
</div>
</form>
</div>
</div>
<!-- Toast Notification -->
<div class="toast" id="toast">
<i class="fa-solid fa-check-circle"></i>
<span id="toastMessage">Password copied to clipboard!</span>
</div>
<script>
// Mock Data - In a real app, this would be encrypted localStorage or IndexedDB
let passwords = [
{ id: 1, site: 'iCloud', username: 'user@example.com', password: 'icloud_secure_pass_123', category: 'email' },
{ id: 2, site: 'Gmail', username: 'john.doe@gmail.com', password: 'super_secret_email_pass', category: 'email' },
{ id: 3, site: 'Netflix', username: 'john.doe@gmail.com', password: 'netflix_stream_2023', category: 'other' },
{ id: 4, site: 'Bank Portal', username: 'client_88291', password: 'weak', category: 'finance' }
];
// DOM Elements
const container = document.getElementById('password-container');
const totalDisplay = document.getElementById('total-count');
const weakDisplay = document.getElementById('weak-count');
const lastUpdatedDisplay = document.getElementById('last-updated');
const modal = document.getElementById('addModal');
const form = document.getElementById('addForm');
// Initialize
document.addEventListener('DOMContentLoaded', () => {
renderPasswords();
lastUpdatedDisplay.textContent = new Date().toLocaleDateString();
});
// Render Function
function renderPasswords() {
container.innerHTML = '';
if (passwords.length === 0) {
container.innerHTML = `
<div style="grid-column: 1/-1; text-align: center; padding: 3rem; background: #1e293b; border-radius: var(--border-radius); border: 2px dashed #334155;">
<i class="fa-solid fa-lock-open" style="font-size: 3rem; color: var(--text-muted); margin-bottom: 1rem;"></i>
<h3>Nosaved credentials</h3>
<p style="color: var(--text-muted);">Click "Add Account" to securely store your first login.</p>
</div>`;
totalDisplay.textContent = 0;
weakDisplay.textContent = 0;
return;
}
let weakCount = 0;
passwords.forEach(item => {
// Check for weak passwords (simple logic for demo)
if (item.password.length < 8 || item.password.toLowerCase() === item.password || item.password === 'weak') {
weakCount++;
}
const card = document.createElement('div');
card.className = 'password-card';
// Determine icon based on category
let iconClass = 'fa-globe';
if (item.category === 'email') iconClass = 'fa-envelope';
if (item.category === 'finance') iconClass = 'fa-credit-card';
if (item.category === 'social') iconClass = 'fa-users';
card.innerHTML = `
<div class="card-header">
<div class="site-name">
<div class="site-icon">
<i class="fa-solid ${iconClass}"></i>
</div>
${escapeHtml(item.site)}
</div>
<div class="card-actions">
<button class="btn-icon" onclick="editPassword(${item.id})" title="Edit">
<i class="fa-solid fa-pen-to-square"></i>
</button>
<button class="btn-icon btn-delete" onclick="deletePassword(${item.id})" title="Delete">
<i class="fa-solid fa-trash"></i>
</button>
</div>
</div>
<div class="credential-row">
<span class="label">Username / Email</span>
<div class="input-group">
<input type="text" value="${escapeHtml(item.username)}" readonly>
<button class="btn-copy" onclick="copyToClipboard('${escapeHtml(item.username)}', this)">
<i class="fa-solid fa-copy"></i> Copy
</button>
</div>
</div>
<div class="credential-row">
<span class="label">Password</span>
<div class="input-group">
<input type="password" id="pass-${item.id}" value="${item.password}" readonly>
<button class="toggle-password" onclick="togglePasswordVisibility('pass-${item.id}')">
<i class="fa-solid fa-eye" id="icon-${item.id}"></i>
</button>
<button class="btn-copy" onclick="copyToClipboard('${item.password}', this)">
<i class="fa-solid fa-copy"></i> Copy
</button>
</div>
${item.password.length < 8 ? '<span style="color: var(--danger); font-size: 0.75rem; margin-top: 5px; display:block;"><i class="fa-solid fa-triangle-exclamation"></i> Weak Password</span>' : ''}
</div>
`;
container.appendChild(card);
});
totalDisplay.textContent = passwords.length;
weakDisplay.textContent = weakCount;
}
// Helper: Escape HTML to prevent XSS
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
// Toggle Password Visibility
function togglePasswordVisibility(inputId) {
const input = document.getElementById(inputId);
const icon = document.getElementById(inputId.replace('pass-', 'icon-'));
if (input.type === 'password') {
input.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
input.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}
// Copy to Clipboard
function copyToClipboard(text, btnElement) {
navigator.clipboard.writeText(text).then(() => {
showToast('Copied to clipboard!');
// Visual feedback on button
const originalHtml = btnElement.innerHTML;
btnElement.innerHTML = '<i class="fa-solid fa-check"></i> Copied';
setTimeout(() => {
btnElement.innerHTML = originalHtml;
}, 2000);
}).catch(err => {
showToast('Failed to copy');
});
}
// Add New Password
form.addEventListener('submit', (e) => {
e.preventDefault();
const site = document.getElementById('siteName').value;
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const category = document.getElementById('category').value;
const newEntry = {
id: Date.now(),
site,
username,
password,
category
};
passwords.push(newEntry);
renderPasswords();
closeModal();
showToast('New credential added securely!');
});
// Delete Password
window.deletePassword = function(id) {
if (confirm('Are you sure you want to delete this credential?')) {
passwords = passwords.filter(item => item.id !== id);
renderPasswords();
showToast('Credential deleted.');
}
};
// Edit Password (Simplified)
window.editPassword = function(id) {
const item = passwords.find(p => p.id === id);
if (item) {
document.getElementById('siteName').value = item.site;
document.getElementById('username').value = item.username;
document.getElementById('password').value = item.password;
document.getElementById('category').value = item.category;
// Remove the old item temporarily
passwords = passwords.filter(p => p.id !== id);
renderPasswords();
openModal();
// Note: In a full app, this would be a better form state management
}
};
// Modal Logic
function openModal() {
modal.style.display = 'flex';
}
function closeModal() {
modal.style.display = 'none';
form.reset();
}
// Close modal if clicking outside
window.onclick = function(event) {
if (event.target == modal) {
closeModal();
}
}
// Toast Logic
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
function showToast(message) {
toastMessage.textContent = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
</script>
</body>
</html>