TemporaryNote-Pad / index.html
AmitJ82's picture
Update index.html
8f978b6 verified
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Enigma Vault - Secure Note & Password Pad</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- Google Fonts (Poppins) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Poppins', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #1e293b; }
::-webkit-scrollbar-thumb { background: #475569; border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: #64748b; }
#note-body {
height: calc(100vh - 10rem); /* Adjust height dynamically */
}
.strength-bar {
transition: all 0.3s ease-in-out;
}
/* Fix for password overflow */
#generated-password {
overflow-wrap: break-word;
word-break: break-all;
}
</style>
</head>
<body class="bg-slate-900 text-slate-300">
<div class="flex h-screen">
<!-- Sidebar -->
<aside class="w-1/3 md:w-1/4 lg:w-1/5 bg-slate-800/70 p-4 flex flex-col border-r border-slate-700">
<header class="mb-4">
<h1 class="text-2xl font-bold text-white text-center">Enigma Vault</h1>
<p class="text-xs text-slate-400 text-center mb-4">Your Private Notes & Passwords</p>
<button id="new-note-btn" class="w-full bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-lg transition duration-300 flex items-center justify-center gap-2">
<i class="fas fa-plus"></i> New Note
</button>
</header>
<div id="note-list" class="flex-grow overflow-y-auto space-y-2">
<!-- Notes will be dynamically inserted here -->
</div>
<footer class="mt-4 pt-4 border-t border-slate-700 space-y-2">
<button id="open-password-generator-btn" class="w-full bg-slate-700 hover:bg-slate-600 text-white font-bold py-2 px-4 rounded-lg transition duration-300 flex items-center justify-center gap-2">
<i class="fas fa-key"></i> Password Generator
</button>
<button id="download-all-btn" class="w-full bg-slate-700 hover:bg-slate-600 text-white font-bold py-2 px-4 rounded-lg transition duration-300 flex items-center justify-center gap-2">
<i class="fas fa-file-archive"></i> Download All Notes
</button>
<div id="privacy-notice" class="text-center text-xs text-slate-500 mt-2 p-2">
<i class="fas fa-exclamation-triangle"></i> <strong>Temporary Pad.</strong> All notes are deleted if you refresh or close the page. Nothing is saved permanently.
</div>
</footer>
</aside>
<!-- Main Content -->
<main class="w-2/3 md:w-3/4 lg:w-4/5 p-4 sm:p-6 md:p-8">
<!-- Welcome Screen -->
<div id="welcome-screen" class="flex flex-col items-center justify-center h-full text-center">
<i class="fas fa-book-open text-6xl text-slate-600 mb-4"></i>
<h2 class="text-3xl font-bold text-white">Welcome to Enigma Vault</h2>
<p class="text-slate-400 mt-2">Select a note from the list or create a new one to get started.</p>
</div>
<!-- Note Editor -->
<div id="note-editor" class="hidden h-full flex flex-col">
<div class="flex justify-between items-center mb-4 gap-4">
<input type="text" id="note-title" class="w-full bg-transparent text-3xl font-bold text-white focus:outline-none" placeholder="Note Title">
<div class="flex items-center gap-2 flex-shrink-0">
<button id="download-note-btn" title="Download this note" class="text-green-500 hover:text-green-400 p-2 rounded-full">
<i class="fas fa-download fa-lg"></i>
</button>
<button id="delete-note-btn" title="Delete this note" class="text-red-500 hover:text-red-400 p-2 rounded-full">
<i class="fas fa-trash-alt fa-lg"></i>
</button>
</div>
</div>
<textarea id="note-body" class="w-full flex-grow bg-slate-800/50 rounded-lg p-4 text-slate-200 focus:outline-none focus:ring-2 focus:ring-green-500" placeholder="Start writing your secure thoughts..."></textarea>
</div>
</main>
</div>
<!-- Password Generator Modal -->
<div id="password-generator-modal" class="hidden fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center p-4 z-50">
<div class="bg-slate-800 rounded-xl shadow-lg p-6 w-full max-w-md border-t-4 border-amber-500">
<div class="flex justify-between items-center mb-4">
<h2 class="text-2xl font-bold text-amber-400">Password Generator</h2>
<button id="close-modal-btn" class="text-slate-400 hover:text-white text-3xl leading-none">×</button>
</div>
<!-- Generated Password Display -->
<div class="bg-slate-900 p-4 rounded-lg mb-4 flex items-start min-h-[80px]">
<span id="generated-password" class="text-xl font-mono text-white flex-grow">Click Generate...</span>
<button id="copy-password-btn" class="ml-4 text-slate-400 hover:text-amber-400 flex-shrink-0"><i class="fas fa-copy"></i></button>
</div>
<!-- Strength Meter -->
<div class="w-full bg-slate-700 rounded-full h-2.5 mb-2">
<div id="strength-bar" class="bg-red-600 h-2.5 rounded-full strength-bar" style="width: 10%"></div>
</div>
<p id="strength-text" class="text-center text-sm text-slate-400 mb-6">Strength: Very Weak</p>
<!-- Options -->
<div class="space-y-4">
<div>
<label for="length" class="flex justify-between font-semibold">Length: <span id="length-val">16</span></label>
<input id="length" type="range" min="6" max="32" value="16" class="w-full h-2 bg-slate-700 rounded-lg appearance-none cursor-pointer">
</div>
<div class="grid grid-cols-2 gap-4">
<label class="flex items-center"><input type="checkbox" id="uppercase" class="w-4 h-4" checked><span class="ml-2">Uppercase (A-Z)</span></label>
<label class="flex items-center"><input type="checkbox" id="lowercase" class="w-4 h-4" checked><span class="ml-2">Lowercase (a-z)</span></label>
<label class="flex items-center"><input type="checkbox" id="numbers" class="w-4 h-4" checked><span class="ml-2">Numbers (0-9)</span></label>
<label class="flex items-center"><input type="checkbox" id="symbols" class="w-4 h-4" checked><span class="ml-2">Symbols (!@#$)</span></label>
</div>
<button id="generate-btn" class="w-full bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 rounded-lg transition">Generate New Password</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// DOM Elements
const noteListEl = document.getElementById('note-list');
const newNoteBtn = document.getElementById('new-note-btn');
const deleteNoteBtn = document.getElementById('delete-note-btn');
const downloadNoteBtn = document.getElementById('download-note-btn');
const downloadAllBtn = document.getElementById('download-all-btn');
const noteEditorEl = document.getElementById('note-editor');
const welcomeScreenEl = document.getElementById('welcome-screen');
const noteTitleEl = document.getElementById('note-title');
const noteBodyEl = document.getElementById('note-body');
// State
let notes = [];
let activeNoteId = null;
// --- Core Note Functions ---
const saveNotes = () => {
updateButtonStates();
};
const renderNoteList = () => {
noteListEl.innerHTML = '';
if (notes.length === 0) {
noteListEl.innerHTML = `<p class="text-center text-slate-500 text-sm p-4">No notes yet. Create one!</p>`;
return;
}
const sortedNotes = [...notes].sort((a,b) => b.id - a.id);
sortedNotes.forEach(note => {
const noteItem = document.createElement('div');
noteItem.dataset.id = note.id;
noteItem.className = `p-3 rounded-lg cursor-pointer transition ${note.id === activeNoteId ? 'bg-green-600/30' : 'hover:bg-slate-700'}`;
const title = document.createElement('h3');
title.textContent = note.title || 'Untitled Note';
title.className = 'font-bold text-white truncate';
noteItem.appendChild(title);
const excerpt = document.createElement('p');
excerpt.textContent = note.body.substring(0, 40) + (note.body.length > 40 ? '...' : '');
excerpt.className = 'text-sm text-slate-400 truncate';
noteItem.appendChild(excerpt);
noteItem.addEventListener('click', () => selectNote(note.id));
noteListEl.appendChild(noteItem);
});
};
const selectNote = (id) => {
activeNoteId = id;
const note = notes.find(n => n.id === id);
if (note) {
welcomeScreenEl.classList.add('hidden');
noteEditorEl.classList.remove('hidden');
noteTitleEl.value = note.title;
noteBodyEl.value = note.body;
}
renderNoteList();
};
const createNewNote = () => {
const newNote = {
id: Date.now(),
title: 'New Note',
body: ''
};
notes.push(newNote);
saveNotes();
selectNote(newNote.id);
};
const deleteActiveNote = () => {
if (!activeNoteId) return;
if(confirm('Are you sure you want to delete this note? This action cannot be undone.')){
notes = notes.filter(note => note.id !== activeNoteId);
activeNoteId = null;
saveNotes();
noteEditorEl.classList.add('hidden');
welcomeScreenEl.classList.remove('hidden');
renderNoteList();
}
};
const downloadActiveNote = () => {
if (!activeNoteId) return;
const note = notes.find(n => n.id === activeNoteId);
if (!note) return;
const content = `Title: ${note.title}\n\n---\n\n${note.body}`;
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
const filename = (note.title || 'Untitled Note').replace(/[^a-z0-9]/gi, '_').toLowerCase();
a.href = url;
a.download = `${filename}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
const downloadAllNotes = () => {
if (notes.length === 0) {
alert('There are no notes to download.');
return;
}
const zip = new JSZip();
notes.forEach(note => {
const content = `Title: ${note.title}\n\n---\n\n${note.body}`;
const filename = (note.title || 'Untitled Note').replace(/[^a-z0-9]/gi, '_').toLowerCase();
zip.file(`${filename}_${note.id}.txt`, content);
});
zip.generateAsync({ type: "blob" })
.then(function(blob) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'enigma-vault-notes.zip';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
};
const updateButtonStates = () => {
if (notes.length === 0) {
downloadAllBtn.disabled = true;
downloadAllBtn.classList.add('opacity-50', 'cursor-not-allowed');
} else {
downloadAllBtn.disabled = false;
downloadAllBtn.classList.remove('opacity-50', 'cursor-not-allowed');
}
};
// Event Listeners for Notes
newNoteBtn.addEventListener('click', createNewNote);
deleteNoteBtn.addEventListener('click', deleteActiveNote);
downloadNoteBtn.addEventListener('click', downloadActiveNote);
downloadAllBtn.addEventListener('click', downloadAllNotes);
noteTitleEl.addEventListener('input', () => {
const note = notes.find(n => n.id === activeNoteId);
if(note) {
note.title = noteTitleEl.value;
saveNotes();
const noteInList = noteListEl.querySelector(`[data-id='${activeNoteId}'] h3`);
if(noteInList) noteInList.textContent = note.title || 'Untitled Note';
}
});
noteBodyEl.addEventListener('input', () => {
const note = notes.find(n => n.id === activeNoteId);
if(note) {
note.body = noteBodyEl.value;
saveNotes();
const noteInList = noteListEl.querySelector(`[data-id='${activeNoteId}'] p`);
if(noteInList) noteInList.textContent = note.body.substring(0, 40) + (note.body.length > 40 ? '...' : '');
}
});
// --- Password Generator Logic ---
const openModalBtn = document.getElementById('open-password-generator-btn');
const closeModalBtn = document.getElementById('close-modal-btn');
const modal = document.getElementById('password-generator-modal');
const generateBtn = document.getElementById('generate-btn');
const copyBtn = document.getElementById('copy-password-btn');
const lengthSlider = document.getElementById('length');
const lengthVal = document.getElementById('length-val');
const uppercaseCheck = document.getElementById('uppercase');
const lowercaseCheck = document.getElementById('lowercase');
const numbersCheck = document.getElementById('numbers');
const symbolsCheck = document.getElementById('symbols');
const passwordEl = document.getElementById('generated-password');
const strengthBar = document.getElementById('strength-bar');
const strengthText = document.getElementById('strength-text');
openModalBtn.addEventListener('click', () => modal.classList.remove('hidden'));
closeModalBtn.addEventListener('click', () => modal.classList.add('hidden'));
lengthSlider.addEventListener('input', (e) => {
lengthVal.textContent = e.target.value;
});
copyBtn.addEventListener('click', () => {
if (passwordEl.textContent.length > 0 && passwordEl.textContent !== 'Click Generate...') {
navigator.clipboard.writeText(passwordEl.textContent);
copyBtn.innerHTML = '<i class="fas fa-check text-green-500"></i>';
setTimeout(() => {
copyBtn.innerHTML = '<i class="fas fa-copy"></i>';
}, 1500);
}
});
generateBtn.addEventListener('click', () => {
const length = +lengthSlider.value;
const hasUpper = uppercaseCheck.checked;
const hasLower = lowercaseCheck.checked;
const hasNumber = numbersCheck.checked;
const hasSymbol = symbolsCheck.checked;
passwordEl.textContent = generatePassword(hasUpper, hasLower, hasNumber, hasSymbol, length);
updatePasswordStrength();
});
const generatePassword = (upper, lower, number, symbol, length) => {
let generatedPassword = '';
const typesCount = upper + lower + number + symbol;
const typesArr = [{upper}, {lower}, {number}, {symbol}].filter(item => Object.values(item)[0]);
if(typesCount === 0) return 'Select at least one option';
for(let i=0; i<length; i++) {
const funcName = Object.keys(typesArr[Math.floor(Math.random() * typesArr.length)])[0];
generatedPassword += randomFunc[funcName]();
}
return generatedPassword.slice(0, length);
}
const randomFunc = {
lower: () => String.fromCharCode(Math.floor(Math.random() * 26) + 97),
upper: () => String.fromCharCode(Math.floor(Math.random() * 26) + 65),
number: () => String.fromCharCode(Math.floor(Math.random() * 10) + 48),
symbol: () => '!@#$%^&*(){}[]=<>/,.'.charAt(Math.floor(Math.random() * 20))
};
const updatePasswordStrength = () => {
const password = passwordEl.textContent;
let score = 0;
if(password.length === 0 || password === 'Select at least one option') {
strengthText.textContent = `Strength: N/A`;
strengthBar.style.width = '0%';
return;
}
if (password.length > 10) score++;
if (password.length > 15) score++;
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) score++;
if (/\d/.test(password)) score++;
if (/[^A-Za-z0-9]/.test(password)) score++;
let strength = { text: 'Very Weak', color: 'bg-red-600', width: '10%' };
if (score >= 5) strength = { text: 'Very Strong', color: 'bg-green-500', width: '100%' };
else if (score === 4) strength = { text: 'Strong', color: 'bg-green-400', width: '80%' };
else if (score === 3) strength = { text: 'Medium', color: 'bg-yellow-500', width: '60%' };
else if (score >= 1) strength = { text: 'Weak', color: 'bg-orange-500', width: '35%' };
strengthText.textContent = `Strength: ${strength.text}`;
strengthBar.style.width = strength.width;
strengthBar.className = `h-2.5 rounded-full strength-bar ${strength.color}`;
};
// --- Initial Load ---
const init = () => {
renderNoteList();
updateButtonStates();
lengthVal.textContent = lengthSlider.value; // Set initial slider value display
};
init();
});
</script>
</body>
</html>