| | <!DOCTYPE html> |
| | <html lang="ar" dir="rtl"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>ملاحظاتي - My Notes</title> |
| | |
| | |
| | <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=Cairo:wght@300;400;600;700&display=swap" rel="stylesheet"> |
| |
|
| | <style> |
| | |
| | |
| | |
| | :root { |
| | --primary-color: #6c5ce7; |
| | --primary-hover: #5649c0; |
| | --secondary-color: #a29bfe; |
| | --bg-color: #f0f3f8; |
| | --card-bg: #ffffff; |
| | --text-main: #2d3436; |
| | --text-muted: #636e72; |
| | --danger-color: #ff7675; |
| | --shadow-soft: 0 10px 20px rgba(0,0,0,0.05); |
| | --shadow-hover: 0 15px 30px rgba(0,0,0,0.1); |
| | --radius-lg: 20px; |
| | --radius-md: 12px; |
| | --radius-sm: 8px; |
| | --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); |
| | } |
| | |
| | * { |
| | margin: 0; |
| | padding: 0; |
| | box-sizing: border-box; |
| | font-family: 'Cairo', sans-serif; |
| | } |
| | |
| | body { |
| | background-color: var(--bg-color); |
| | color: var(--text-main); |
| | min-height: 100vh; |
| | padding-bottom: 80px; |
| | background-image: radial-gradient(circle at 10% 20%, rgba(108, 92, 231, 0.05) 0%, transparent 20%), |
| | radial-gradient(circle at 90% 80%, rgba(162, 155, 254, 0.05) 0%, transparent 20%); |
| | } |
| | |
| | |
| | |
| | |
| | .container { |
| | max-width: 1000px; |
| | margin: 0 auto; |
| | padding: 20px; |
| | } |
| | |
| | header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 30px; |
| | flex-wrap: wrap; |
| | gap: 15px; |
| | } |
| | |
| | .brand { |
| | display: flex; |
| | align-items: center; |
| | gap: 10px; |
| | } |
| | |
| | .brand h1 { |
| | font-size: 1.8rem; |
| | font-weight: 700; |
| | color: var(--primary-color); |
| | } |
| | |
| | .anycoder-link { |
| | font-size: 0.8rem; |
| | color: var(--text-muted); |
| | text-decoration: none; |
| | background: rgba(255,255,255,0.6); |
| | padding: 4px 10px; |
| | border-radius: 20px; |
| | transition: var(--transition); |
| | } |
| | .anycoder-link:hover { |
| | background: var(--primary-color); |
| | color: white; |
| | } |
| | |
| | |
| | |
| | |
| | .controls { |
| | display: flex; |
| | gap: 15px; |
| | background: var(--card-bg); |
| | padding: 10px; |
| | border-radius: var(--radius-lg); |
| | box-shadow: var(--shadow-soft); |
| | margin-bottom: 30px; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .search-box { |
| | flex: 1; |
| | position: relative; |
| | min-width: 200px; |
| | } |
| | |
| | .search-box input { |
| | width: 100%; |
| | padding: 12px 45px 12px 15px; |
| | border: 2px solid transparent; |
| | background: var(--bg-color); |
| | border-radius: var(--radius-md); |
| | font-size: 1rem; |
| | transition: var(--transition); |
| | outline: none; |
| | } |
| | |
| | .search-box input:focus { |
| | background: white; |
| | border-color: var(--secondary-color); |
| | } |
| | |
| | .search-icon { |
| | position: absolute; |
| | right: 15px; |
| | top: 50%; |
| | transform: translateY(-50%); |
| | color: var(--text-muted); |
| | } |
| | |
| | .btn { |
| | padding: 12px 24px; |
| | border: none; |
| | border-radius: var(--radius-md); |
| | font-size: 0.95rem; |
| | font-weight: 600; |
| | cursor: pointer; |
| | transition: var(--transition); |
| | display: flex; |
| | align-items: center; |
| | gap: 8px; |
| | } |
| | |
| | .btn-primary { |
| | background-color: var(--primary-color); |
| | color: white; |
| | } |
| | |
| | .btn-primary:hover { |
| | background-color: var(--primary-hover); |
| | transform: translateY(-2px); |
| | box-shadow: 0 5px 15px rgba(108, 92, 231, 0.3); |
| | } |
| | |
| | .btn-danger { |
| | background-color: #ffeaea; |
| | color: var(--danger-color); |
| | } |
| | .btn-danger:hover { |
| | background-color: var(--danger-color); |
| | color: white; |
| | } |
| | |
| | |
| | |
| | |
| | .notes-grid { |
| | display: grid; |
| | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| | gap: 20px; |
| | } |
| | |
| | .note-card { |
| | background: var(--card-bg); |
| | border-radius: var(--radius-lg); |
| | padding: 20px; |
| | box-shadow: var(--shadow-soft); |
| | transition: var(--transition); |
| | position: relative; |
| | display: flex; |
| | flex-direction: column; |
| | border: 1px solid rgba(255,255,255,0.5); |
| | animation: fadeIn 0.4s ease-out; |
| | } |
| | |
| | @keyframes fadeIn { |
| | from { opacity: 0; transform: translateY(20px); } |
| | to { opacity: 1; transform: translateY(0); } |
| | } |
| | |
| | .note-card:hover { |
| | transform: translateY(-5px); |
| | box-shadow: var(--shadow-hover); |
| | } |
| | |
| | .note-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: flex-start; |
| | margin-bottom: 12px; |
| | } |
| | |
| | .note-title { |
| | font-size: 1.2rem; |
| | font-weight: 700; |
| | color: var(--text-main); |
| | word-break: break-word; |
| | } |
| | |
| | .note-date { |
| | font-size: 0.8rem; |
| | color: var(--text-muted); |
| | background: var(--bg-color); |
| | padding: 4px 8px; |
| | border-radius: 6px; |
| | margin-top: 5px; |
| | display: inline-block; |
| | } |
| | |
| | .note-content { |
| | color: var(--text-muted); |
| | line-height: 1.6; |
| | font-size: 0.95rem; |
| | margin-bottom: 20px; |
| | flex-grow: 1; |
| | white-space: pre-wrap; |
| | display: -webkit-box; |
| | -webkit-line-clamp: 6; |
| | -webkit-box-orient: vertical; |
| | overflow: hidden; |
| | } |
| | |
| | .note-actions { |
| | display: flex; |
| | justify-content: flex-end; |
| | gap: 10px; |
| | margin-top: auto; |
| | border-top: 1px solid #f0f0f0; |
| | padding-top: 15px; |
| | } |
| | |
| | .action-btn { |
| | background: none; |
| | border: none; |
| | cursor: pointer; |
| | color: var(--text-muted); |
| | padding: 5px; |
| | border-radius: 50%; |
| | transition: var(--transition); |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | } |
| | |
| | .action-btn:hover { |
| | background-color: var(--bg-color); |
| | color: var(--primary-color); |
| | } |
| | |
| | .action-btn.delete:hover { |
| | color: var(--danger-color); |
| | background-color: #ffeaea; |
| | } |
| | |
| | |
| | .empty-state { |
| | text-align: center; |
| | padding: 60px 20px; |
| | color: var(--text-muted); |
| | grid-column: 1 / -1; |
| | } |
| | |
| | .empty-state svg { |
| | width: 100px; |
| | height: 100px; |
| | margin-bottom: 20px; |
| | opacity: 0.5; |
| | } |
| | |
| | |
| | |
| | |
| | .modal-overlay { |
| | position: fixed; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background: rgba(0, 0, 0, 0.5); |
| | backdrop-filter: blur(5px); |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 1000; |
| | opacity: 0; |
| | visibility: hidden; |
| | transition: var(--transition); |
| | } |
| | |
| | .modal-overlay.active { |
| | opacity: 1; |
| | visibility: visible; |
| | } |
| | |
| | .modal { |
| | background: white; |
| | width: 90%; |
| | max-width: 500px; |
| | border-radius: var(--radius-lg); |
| | padding: 30px; |
| | box-shadow: 0 20px 40px rgba(0,0,0,0.2); |
| | transform: scale(0.9); |
| | transition: var(--transition); |
| | position: relative; |
| | } |
| | |
| | .modal-overlay.active .modal { |
| | transform: scale(1); |
| | } |
| | |
| | .modal h2 { |
| | margin-bottom: 20px; |
| | color: var(--primary-color); |
| | } |
| | |
| | .form-group { |
| | margin-bottom: 20px; |
| | } |
| | |
| | .form-group label { |
| | display: block; |
| | margin-bottom: 8px; |
| | font-weight: 600; |
| | color: var(--text-main); |
| | } |
| | |
| | .form-group input, |
| | .form-group textarea { |
| | width: 100%; |
| | padding: 12px; |
| | border: 2px solid #eee; |
| | border-radius: var(--radius-md); |
| | font-size: 1rem; |
| | transition: var(--transition); |
| | outline: none; |
| | font-family: 'Cairo', sans-serif; |
| | } |
| | |
| | .form-group textarea { |
| | resize: vertical; |
| | min-height: 120px; |
| | } |
| | |
| | .form-group input:focus, |
| | .form-group textarea:focus { |
| | border-color: var(--primary-color); |
| | } |
| | |
| | .modal-actions { |
| | display: flex; |
| | justify-content: flex-end; |
| | gap: 10px; |
| | } |
| | |
| | .btn-secondary { |
| | background: #eee; |
| | color: var(--text-main); |
| | } |
| | .btn-secondary:hover { |
| | background: #ddd; |
| | } |
| | |
| | |
| | |
| | |
| | .fab { |
| | position: fixed; |
| | bottom: 30px; |
| | left: 30px; |
| | width: 60px; |
| | height: 60px; |
| | background: var(--primary-color); |
| | color: white; |
| | border-radius: 50%; |
| | display: none; |
| | justify-content: center; |
| | align-items: center; |
| | box-shadow: 0 10px 20px rgba(108, 92, 231, 0.4); |
| | cursor: pointer; |
| | z-index: 900; |
| | transition: var(--transition); |
| | border: none; |
| | } |
| | |
| | .fab:hover { |
| | transform: scale(1.1); |
| | } |
| | |
| | @media (max-width: 768px) { |
| | .fab { |
| | display: flex; |
| | } |
| | .controls { |
| | flex-direction: column; |
| | } |
| | .btn-danger { |
| | display: none; |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | .toast { |
| | position: fixed; |
| | bottom: 20px; |
| | right: 20px; |
| | background: #333; |
| | color: white; |
| | padding: 12px 24px; |
| | border-radius: var(--radius-md); |
| | box-shadow: 0 5px 15px rgba(0,0,0,0.2); |
| | transform: translateY(100px); |
| | transition: transform 0.3s ease; |
| | z-index: 2000; |
| | } |
| | .toast.show { |
| | transform: translateY(0); |
| | } |
| | |
| | </style> |
| | </head> |
| | <body> |
| |
|
| | <div class="container"> |
| | |
| | <header> |
| | <div class="brand"> |
| | <h1>ملاحظاتي</h1> |
| | <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a> |
| | </div> |
| | <button class="btn btn-danger" onclick="clearAllNotes()"> |
| | <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg> |
| | مسح الكل |
| | </button> |
| | </header> |
| |
|
| | |
| | <div class="controls"> |
| | <div class="search-box"> |
| | <svg class="search-icon" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"></circle><path d="M21 21l-4.35-4.35"></path></svg> |
| | <input type="text" id="searchInput" placeholder="ابحث في الملاحظات..." oninput="searchNotes()"> |
| | </div> |
| | <button class="btn btn-primary" onclick="openModal()"> |
| | <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M12 4v16m8-8H4"></path></svg> |
| | ملاحظة جديدة |
| | </button> |
| | </div> |
| |
|
| | |
| | <div class="notes-grid" id="notesContainer"> |
| | |
| | </div> |
| | </div> |
| |
|
| | |
| | <button class="fab" onclick="openModal()"> |
| | <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M12 4v16m8-8H4"></path></svg> |
| | </button> |
| |
|
| | |
| | <div class="modal-overlay" id="noteModal"> |
| | <div class="modal"> |
| | <h2 id="modalTitle">ملاحظة جديدة</h2> |
| | <input type="hidden" id="noteId"> |
| | |
| | <div class="form-group"> |
| | <label for="noteTitleInput">العنوان</label> |
| | <input type="text" id="noteTitleInput" placeholder="اكتب عنوان الملاحظة..."> |
| | </div> |
| | |
| | <div class="form-group"> |
| | <label for="noteContentInput">المحتوى</label> |
| | <textarea id="noteContentInput" placeholder="اكتب تفاصيل الملاحظة هنا..."></textarea> |
| | </div> |
| |
|
| | <div class="modal-actions"> |
| | <button class="btn btn-secondary" onclick="closeModal()">إلغاء</button> |
| | <button class="btn btn-primary" onclick="saveNote()">حفظ</button> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="toast" id="toast">تمت العملية بنجاح</div> |
| |
|
| | <script> |
| | |
| | |
| | |
| | let notes = []; |
| | |
| | |
| | |
| | |
| | document.addEventListener('DOMContentLoaded', () => { |
| | loadNotes(); |
| | renderNotes(); |
| | }); |
| | |
| | |
| | |
| | |
| | |
| | |
| | function loadNotes() { |
| | const storedNotes = localStorage.getItem('myAppNotes'); |
| | if (storedNotes) { |
| | notes = JSON.parse(storedNotes); |
| | } |
| | } |
| | |
| | |
| | function saveToLocalStorage() { |
| | localStorage.setItem('myAppNotes', JSON.stringify(notes)); |
| | } |
| | |
| | |
| | function renderNotes(filterText = '') { |
| | const container = document.getElementById('notesContainer'); |
| | container.innerHTML = ''; |
| | |
| | |
| | const filteredNotes = notes.filter(note => |
| | note.title.toLowerCase().includes(filterText.toLowerCase()) || |
| | note.content.toLowerCase().includes(filterText.toLowerCase()) |
| | ); |
| | |
| | |
| | filteredNotes.sort((a, b) => new Date(b.date) - new Date(a.date)); |
| | |
| | if (filteredNotes.length === 0) { |
| | container.innerHTML = ` |
| | <div class="empty-state"> |
| | <svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path> |
| | </svg> |
| | <h3>لا توجد ملاحظات</h3> |
| | <p>ابدأ بإضافة ملاحظتك الأولى الآن</p> |
| | </div> |
| | `; |
| | return; |
| | } |
| | |
| | filteredNotes.forEach(note => { |
| | const card = document.createElement('div'); |
| | card.className = 'note-card'; |
| | card.innerHTML = ` |
| | <div class="note-header"> |
| | <div class="note-title">${escapeHtml(note.title)}</div> |
| | </div> |
| | <div class="note-date">${formatDate(note.date)}</div> |
| | <div class="note-content">${escapeHtml(note.content)}</div> |
| | <div class="note-actions"> |
| | <button class="action-btn" onclick="editNote(${note.id})" title="تعديل"> |
| | <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg> |
| | </button> |
| | <button class="action-btn delete" onclick="deleteNote(${note.id})" title="حذف"> |
| | <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg> |
| | </button> |
| | </div> |
| | `; |
| | container.appendChild(card); |
| | }); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | function saveNote() { |
| | const idInput = document.getElementById('noteId').value; |
| | const titleInput = document.getElementById('noteTitleInput').value; |
| | const contentInput = document.getElementById('noteContentInput').value; |
| | |
| | if (!titleInput.trim() && !contentInput.trim()) { |
| | showToast('الرجاء كتابة عنوان أو محتوى'); |
| | return; |
| | } |
| | |
| | if (idInput) { |
| | |
| | const noteIndex = notes.findIndex(n => n.id == idInput); |
| | if (noteIndex > -1) { |
| | notes[noteIndex].title = titleInput; |
| | notes[noteIndex].content = contentInput; |
| | notes[noteIndex].date = new Date().toISOString(); |
| | showToast('تم تعديل الملاحظة'); |
| | } |
| | } else { |
| | |
| | const newNote = { |
| | id: Date.now(), |
| | title: titleInput || 'بدون عنوان', |
| | content: contentInput, |
| | date: new Date().toISOString() |
| | }; |
| | notes.push(newNote); |
| | showToast('تمت إضافة الملاحظة'); |
| | } |
| | |
| | saveToLocalStorage(); |
| | renderNotes(document.getElementById('searchInput').value); |
| | closeModal(); |
| | } |
| | |
| | function deleteNote(id) { |
| | if(confirm('هل أنت متأكد من حذف هذه الملاحظة؟')) { |
| | notes = notes.filter(n => n.id !== id); |
| | saveToLocalStorage(); |
| | renderNotes(document.getElementById('searchInput').value); |
| | showToast('تم حذف الملاحظة'); |
| | } |
| | } |
| | |
| | function clearAllNotes() { |
| | if(notes.length === 0) return; |
| | if(confirm('هل أنت متأكد من مسح جميع الملاحظات؟ لا يمكن التراجع عن هذا الإجراء.')) { |
| | notes = []; |
| | saveToLocalStorage(); |
| | renderNotes(); |
| | showToast('تم مسح جميع الملاحظات'); |
| | } |
| | } |
| | |
| | function editNote(id) { |
| | const note = notes.find(n => n.id === id); |
| | if (note) { |
| | document.getElementById('noteId').value = note.id; |
| | document.getElementById('noteTitleInput').value = note.title; |
| | document.getElementById('noteContentInput').value = note.content; |
| | document.getElementById('modalTitle').innerText = 'تعديل الملاحظة'; |
| | |
| | const modal = document.getElementById('noteModal'); |
| | modal.classList.add('active'); |
| | } |
| | } |
| | |
| | function searchNotes() { |
| | const searchText = document.getElementById('searchInput').value; |
| | renderNotes(searchText); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | function openModal() { |
| | |
| | document.getElementById('noteId').value = ''; |
| | document.getElementById('noteTitleInput').value = ''; |
| | document.getElementById('noteContentInput').value = ''; |
| | document.getElementById('modalTitle').innerText = 'ملاحظة جديدة'; |
| | |
| | document.getElementById('noteModal').classList.add('active'); |
| | document.getElementById('noteTitleInput').focus(); |
| | } |
| | |
| | function closeModal() { |
| | document.getElementById('noteModal').classList.remove('active'); |
| | } |
| | |
| | |
| | document.getElementById('noteModal').addEventListener('click', (e) => { |
| | if (e.target === document.getElementById('noteModal')) { |
| | closeModal(); |
| | } |
| | }); |
| | |
| | |
| | |
| | |
| | |
| | function showToast(message) { |
| | const toast = document.getElementById('toast'); |
| | toast.innerText = message; |
| | toast.classList.add('show'); |
| | setTimeout(() => { |
| | toast.classList.remove('show'); |
| | }, 3000); |
| | } |
| | |
| | function formatDate(dateString) { |
| | const options = { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }; |
| | return new Date(dateString).toLocaleDateString('ar-EG', options); |
| | } |
| | |
| | |
| | function escapeHtml(text) { |
| | if (!text) return ''; |
| | return text |
| | .replace(/&/g, "&") |
| | .replace(/</g, "<") |
| | .replace(/>/g, ">") |
| | .replace(/"/g, """) |
| | .replace(/'/g, "'"); |
| | } |
| | |
| | </script> |
| | </body> |
| | </html> |