Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Advanced Note Taking App</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .note-content { | |
| min-height: 200px; | |
| border: 1px solid #e2e8f0; | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| outline: none; | |
| } | |
| .dark .note-content { | |
| background-color: #1e293b; | |
| border-color: #475569; | |
| color: #f8fafc; | |
| } | |
| .dark .note-content:focus { | |
| border-color: #60a5fa; | |
| } | |
| .ql-toolbar.ql-snow { | |
| border-radius: 0.5rem 0.5rem 0 0; | |
| } | |
| .ql-container.ql-snow { | |
| border-radius: 0 0 0.5rem 0.5rem; | |
| min-height: 200px; | |
| } | |
| .dark .ql-toolbar.ql-snow { | |
| background-color: #1e293b; | |
| border-color: #475569; | |
| } | |
| .dark .ql-container.ql-snow { | |
| background-color: #1e293b; | |
| border-color: #475569; | |
| color: #f8fafc; | |
| } | |
| .dark .ql-editor { | |
| color: #f8fafc; | |
| } | |
| .tag { | |
| display: inline-flex; | |
| align-items: center; | |
| padding: 0.25rem 0.5rem; | |
| border-radius: 9999px; | |
| font-size: 0.75rem; | |
| font-weight: 500; | |
| } | |
| .tag-remove { | |
| margin-left: 0.25rem; | |
| cursor: pointer; | |
| } | |
| .transition-all { | |
| transition: all 0.3s ease; | |
| } | |
| .note-card { | |
| transition: transform 0.2s ease, box-shadow 0.2s ease; | |
| } | |
| .note-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| } | |
| .dark .note-card:hover { | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); | |
| } | |
| </style> | |
| <link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet"> | |
| </head> | |
| <body class="bg-gray-50 dark:bg-gray-900 transition-all"> | |
| <div class="container mx-auto px-4 py-8 max-w-6xl"> | |
| <!-- Header --> | |
| <header class="flex justify-between items-center mb-8"> | |
| <h1 class="text-3xl font-bold text-gray-800 dark:text-white"> | |
| <i class="fas fa-book mr-2"></i> Notes App | |
| </h1> | |
| <div class="flex items-center space-x-4"> | |
| <button id="theme-toggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200"> | |
| <i class="fas fa-moon dark:hidden"></i> | |
| <i class="fas fa-sun hidden dark:inline"></i> | |
| </button> | |
| <button id="new-note-btn" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition-colors"> | |
| <i class="fas fa-plus mr-2"></i> New Note | |
| </button> | |
| </div> | |
| </header> | |
| <!-- Search and Filter --> | |
| <div class="mb-6 flex flex-col md:flex-row gap-4"> | |
| <div class="relative flex-grow"> | |
| <input type="text" id="search-notes" placeholder="Search notes..." | |
| class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-white"> | |
| <i class="fas fa-search absolute right-3 top-3 text-gray-400"></i> | |
| </div> | |
| <div class="flex gap-2"> | |
| <select id="filter-tags" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-white"> | |
| <option value="">All Tags</option> | |
| </select> | |
| <select id="sort-notes" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-white"> | |
| <option value="newest">Newest First</option> | |
| <option value="oldest">Oldest First</option> | |
| <option value="title-asc">Title (A-Z)</option> | |
| <option value="title-desc">Title (Z-A)</option> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Notes List --> | |
| <div class="lg:col-span-1 bg-white dark:bg-gray-800 rounded-lg shadow p-4 h-fit max-h-[600px] overflow-y-auto"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800 dark:text-white">Your Notes</h2> | |
| <div id="notes-list" class="space-y-3"> | |
| <!-- Notes will be added here dynamically --> | |
| <div class="text-center py-8 text-gray-500 dark:text-gray-400"> | |
| <i class="fas fa-sticky-note text-4xl mb-2"></i> | |
| <p>No notes yet. Create your first note!</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Note Editor --> | |
| <div class="lg:col-span-2 bg-white dark:bg-gray-800 rounded-lg shadow p-6"> | |
| <div id="empty-state" class="text-center py-16 text-gray-500 dark:text-gray-400"> | |
| <i class="fas fa-edit text-5xl mb-4"></i> | |
| <h3 class="text-xl font-medium mb-2">Select or create a note</h3> | |
| <p>Start writing your thoughts, ideas, and reminders</p> | |
| </div> | |
| <div id="editor-container" class="hidden"> | |
| <input type="text" id="note-title" placeholder="Note Title" | |
| class="w-full px-4 py-3 text-2xl font-bold border-b border-gray-200 dark:border-gray-700 mb-4 focus:outline-none dark:bg-gray-800 dark:text-white"> | |
| <div class="flex flex-wrap gap-2 mb-4" id="tags-container"> | |
| <input type="text" id="tag-input" placeholder="Add tags..." | |
| class="px-3 py-1 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-white"> | |
| </div> | |
| <!-- Rich Text Editor --> | |
| <div id="editor" class="note-content dark:bg-gray-800"></div> | |
| <div class="flex justify-between items-center mt-6"> | |
| <div class="text-sm text-gray-500 dark:text-gray-400" id="note-date"> | |
| Created: Just now | |
| </div> | |
| <div class="space-x-2"> | |
| <button id="delete-note" class="px-4 py-2 text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg transition-colors"> | |
| <i class="fas fa-trash mr-2"></i> Delete | |
| </button> | |
| <button id="save-note" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition-colors"> | |
| <i class="fas fa-save mr-2"></i> Save | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="https://cdn.quilljs.com/1.3.6/quill.js"></script> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize theme | |
| if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { | |
| document.documentElement.classList.add('dark'); | |
| } else { | |
| document.documentElement.classList.remove('dark'); | |
| } | |
| // Theme toggle | |
| document.getElementById('theme-toggle').addEventListener('click', function() { | |
| document.documentElement.classList.toggle('dark'); | |
| localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light'); | |
| }); | |
| // Initialize Quill editor | |
| const quill = new Quill('#editor', { | |
| theme: 'snow', | |
| modules: { | |
| toolbar: [ | |
| [{ 'header': [1, 2, 3, false] }], | |
| ['bold', 'italic', 'underline', 'strike'], | |
| [{ 'color': [] }, { 'background': [] }], | |
| [{ 'list': 'ordered'}, { 'list': 'bullet' }], | |
| ['link', 'image'], | |
| ['clean'] | |
| ] | |
| }, | |
| placeholder: 'Write your note here...' | |
| }); | |
| // Notes data | |
| let notes = JSON.parse(localStorage.getItem('notes')) || []; | |
| let currentNoteId = null; | |
| let allTags = new Set(); | |
| // Initialize the app | |
| function initApp() { | |
| updateNotesList(); | |
| updateTagFilter(); | |
| // Set up event listeners | |
| document.getElementById('new-note-btn').addEventListener('click', createNewNote); | |
| document.getElementById('save-note').addEventListener('click', saveNote); | |
| document.getElementById('delete-note').addEventListener('click', deleteNote); | |
| document.getElementById('search-notes').addEventListener('input', searchNotes); | |
| document.getElementById('filter-tags').addEventListener('change', filterNotesByTag); | |
| document.getElementById('sort-notes').addEventListener('change', sortNotes); | |
| // Tag input | |
| const tagInput = document.getElementById('tag-input'); | |
| tagInput.addEventListener('keydown', function(e) { | |
| if (e.key === 'Enter' && tagInput.value.trim()) { | |
| addTag(tagInput.value.trim()); | |
| tagInput.value = ''; | |
| } | |
| }); | |
| } | |
| // Create a new note | |
| function createNewNote() { | |
| const newNote = { | |
| id: Date.now().toString(), | |
| title: 'Untitled Note', | |
| content: '', | |
| tags: [], | |
| createdAt: new Date().toISOString(), | |
| updatedAt: new Date().toISOString() | |
| }; | |
| notes.unshift(newNote); | |
| currentNoteId = newNote.id; | |
| localStorage.setItem('notes', JSON.stringify(notes)); | |
| showEditor(newNote); | |
| updateNotesList(); | |
| document.getElementById('note-title').focus(); | |
| } | |
| // Save the current note | |
| function saveNote() { | |
| if (!currentNoteId) return; | |
| const noteIndex = notes.findIndex(note => note.id === currentNoteId); | |
| if (noteIndex === -1) return; | |
| const title = document.getElementById('note-title').value; | |
| const content = quill.root.innerHTML; | |
| const tags = Array.from(document.querySelectorAll('.tag')).map(tag => tag.textContent.replace('×', '').trim()); | |
| notes[noteIndex].title = title; | |
| notes[noteIndex].content = content; | |
| notes[noteIndex].tags = tags; | |
| notes[noteIndex].updatedAt = new Date().toISOString(); | |
| localStorage.setItem('notes', JSON.stringify(notes)); | |
| updateNotesList(); | |
| updateTagFilter(); | |
| // Show success message | |
| const saveBtn = document.getElementById('save-note'); | |
| const originalText = saveBtn.innerHTML; | |
| saveBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Saved!'; | |
| setTimeout(() => { | |
| saveBtn.innerHTML = originalText; | |
| }, 2000); | |
| } | |
| // Delete the current note | |
| function deleteNote() { | |
| if (!currentNoteId || !confirm('Are you sure you want to delete this note?')) return; | |
| notes = notes.filter(note => note.id !== currentNoteId); | |
| localStorage.setItem('notes', JSON.stringify(notes)); | |
| currentNoteId = null; | |
| document.getElementById('editor-container').classList.add('hidden'); | |
| document.getElementById('empty-state').classList.remove('hidden'); | |
| updateNotes | |
| </html> |