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> |