`;
return;
}
// Sort notes by timestamp, newest first
const sortedNotes = [...notes].sort((a, b) => b.timestamp - a.timestamp);
sortedNotes.forEach(note => {
const noteElement = document.createElement('div');
noteElement.className = 'p-4 border rounded-lg bg-gray-50 flex flex-col sm:flex-row justify-between items-start sm:items-center';
const decryptedTitle = decrypt(note.title, sessionPassword) || "Decryption Error";
const date = new Date(note.timestamp).toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' });
noteElement.innerHTML = `
`;
notesList.appendChild(noteElement);
});
};
// --- TAB NAVIGATION LOGIC ---
const updateNavButtons = () => {
prevBtn.style.visibility = currentTab === 0 ? 'hidden' : 'visible';
nextBtn.style.visibility = currentTab === TOTAL_TABS - 1 ? 'hidden' : 'visible';
};
const changeTab = (tabIndex) => {
// IV. B. Guard clause
if (tabIndex < 0 || tabIndex >= TOTAL_TABS) return;
document.getElementById(`tabContent-${currentTab}`).classList.add('hidden');
document.getElementById(`tab-${currentTab}`).classList.remove('tab-active');
document.getElementById(`tab-${currentTab}`).classList.add('tab-inactive');
document.getElementById(`tabContent-${tabIndex}`).classList.remove('hidden');
document.getElementById(`tab-${tabIndex}`).classList.add('tab-active');
document.getElementById(`tab-${tabIndex}`).classList.remove('tab-inactive');
currentTab = tabIndex;
updateNavButtons();
};
window.app.changeTab = changeTab;
const navigateTabs = (direction) => {
const newIndex = direction === 'next' ? currentTab + 1 : currentTab - 1;
changeTab(newIndex);
};
window.app.navigateTabs = navigateTabs;
// --- CRUD & FORM LOGIC ---
const clearForm = () => {
noteForm.reset();
noteIdInput.value = '';
noteFormTitle.textContent = 'Add a New Note';
};
window.app.clearForm = clearForm;
const handleFormSubmit = (e) => {
e.preventDefault();
const id = noteIdInput.value;
const title = noteTitleInput.value;
const content = noteContentInput.value;
const encryptedTitle = encrypt(title, sessionPassword);
const encryptedContent = encrypt(content, sessionPassword);
if (!encryptedTitle || !encryptedContent) {
alert("Error: Could not encrypt note data.");
return;
}
if (id) { // Editing existing note
const noteIndex = notes.findIndex(n => n.id === id);
if (noteIndex > -1) {
notes[noteIndex].title = encryptedTitle;
notes[noteIndex].content = encryptedContent;
notes[noteIndex].timestamp = Date.now();
}
} else { // Creating new note
const newNote = {
id: 'note_' + Date.now(),
title: encryptedTitle,
content: encryptedContent,
timestamp: Date.now()
};
notes.push(newNote);
}
saveNotesToStorage();
renderNotes();
clearForm();
changeTab(0); // Switch to 'My Notes' tab after saving
};
const editNote = (id) => {
const note = notes.find(n => n.id === id);
if (note) {
const decryptedTitle = decrypt(note.title, sessionPassword);
const decryptedContent = decrypt(note.content, sessionPassword);
if (decryptedTitle === null || decryptedContent === null) {
alert("Error decrypting note for editing.");
return;
}
noteIdInput.value = note.id;
noteTitleInput.value = decryptedTitle;
noteContentInput.value = decryptedContent;
noteFormTitle.textContent = 'Edit Note';
changeTab(1);
}
};
window.app.editNote = editNote;
const deleteNote = (id) => {
if (confirm('Are you sure you want to permanently delete this note?')) {
notes = notes.filter(n => n.id !== id);
saveNotesToStorage();
renderNotes();
}
};
window.app.deleteNote = deleteNote;
// --- PDF DOWNLOAD FUNCTIONALITY ---
const downloadNote = (id) => {
const note = notes.find(n => n.id === id);
if (note) {
const title = decrypt(note.title, sessionPassword);
const content = decrypt(note.content, sessionPassword);
if (title === null || content === null) {
alert("Error decrypting note for download.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
// Set document properties and styles
doc.setFont("helvetica", "bold");
doc.setFontSize(22);
doc.setTextColor(40); // Dark gray
doc.text(title, 105, 20, null, null, 'center');
doc.setFont("helvetica", "normal");
doc.setFontSize(12);
doc.setTextColor(80); // Lighter gray
const date = new Date(note.timestamp).toLocaleString('en-US');
doc.text(`Saved on: ${date}`, 105, 30, null, null, 'center');
// Add content with line wrapping
const splitContent = doc.splitTextToSize(content, 180); // 180mm width
doc.text(splitContent, 15, 50);
// Sanitize title for filename
const fileName = `${title.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.pdf`;
doc.save(fileName);
}
};
window.app.downloadNote = downloadNote;
// --- AUTHENTICATION & INITIALIZATION ---
const initialize = () => {
if (localStorage.getItem(STORAGE_KEY)) {
// Vault exists, ask for password
setPasswordContainer.classList.add('hidden');
loginContainer.classList.remove('hidden');
} else {
// No vault, ask to set a password
setPasswordContainer.classList.remove('hidden');
loginContainer.classList.add('hidden');
}
};
setPasswordForm.addEventListener('submit', (e) => {
e.preventDefault();
passwordError.textContent = '';
const newPass = document.getElementById('newPassword').value;
const confirmPass = document.getElementById('confirmPassword').value;
if (newPass.length < 8) {
passwordError.textContent = 'Password must be at least 8 characters long.';
return;
}
if (newPass !== confirmPass) {
passwordError.textContent = 'Passwords do not match.';
return;
}
sessionPassword = newPass;
saveNotesToStorage(); // Save the initial empty vault
passwordOverlay.classList.add('hidden');
mainApp.classList.remove('hidden');
renderNotes();
updateNavButtons();
});
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
passwordError.textContent = '';
const pass = document.getElementById('loginPassword').value;
sessionPassword = pass;
if (loadNotesFromStorage()) {
passwordOverlay.classList.add('hidden');
mainApp.classList.remove('hidden');
renderNotes();
updateNavButtons();
} else {
passwordError.textContent = 'Incorrect password. Please try again.';
sessionPassword = null;
}
});
logoutButton.addEventListener('click', () => {
sessionPassword = null;
notes = [];
mainApp.classList.add('hidden');
passwordOverlay.classList.remove('hidden');
loginForm.reset();
initialize();
});
// IV. C. Event Handling: Attaching primary listeners here
noteForm.addEventListener('submit', handleFormSubmit);
// Initial call to set up the app state
initialize();
});
${decryptedTitle}
Last updated: ${date}
