// ===== Application State ===== let tasks = []; let categories = []; let workspaces = []; let currentView = 'all'; let currentFilter = { priority: 'all', workspace: null, date: null, search: '' }; let currentSort = 'date-desc'; let editingTaskId = null; let viewMode = 'list'; let sortableInstance = null; let lastDeletedTask = null; let focusModeEnabled = false; let smartFilter = 'all'; let calendarMonth = new Date(); let calendarViewVisible = false; let streakCount = 0; let myDayTasks = []; // Pomodoro let pomodoroRunning = false; let pomodoroTimer = null; let pomodoroTime = 25 * 60; let pomodoroPhase = 'focus'; let pomodoroSessions = 0; let pomodoroTodayMinutes = 0; // ===== Initialize ===== document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { document.getElementById('loading-screen').classList.add('hidden'); loadFromLocalStorage(); initializeEventListeners(); setQuickAddDefaults(); renderTasks(); renderCategories(); renderWorkspaces(); updateWorkspaceSelect(); updateCounts(); updateProgress(); updateInsights(); applySettings(); checkDarkMode(); updateUserStats(); loadStreak(); updatePomodoroDisplay(); initParticles(); }, 1200); }); // ===== Local Storage ===== function saveToLocalStorage() { localStorage.setItem('tasks', JSON.stringify(tasks)); localStorage.setItem('categories', JSON.stringify(categories)); localStorage.setItem('workspaces', JSON.stringify(workspaces)); localStorage.setItem('pomodoroSessions', pomodoroSessions.toString()); localStorage.setItem('pomodoroTodayMinutes', pomodoroTodayMinutes.toString()); } function loadFromLocalStorage() { try { tasks = JSON.parse(localStorage.getItem('tasks')) || []; categories = JSON.parse(localStorage.getItem('categories')) || []; workspaces = JSON.parse(localStorage.getItem('workspaces')) || []; pomodoroSessions = parseInt(localStorage.getItem('pomodoroSessions')) || 0; pomodoroTodayMinutes = parseInt(localStorage.getItem('pomodoroTodayMinutes')) || 0; const savedUserName = localStorage.getItem('userName'); if (savedUserName) { document.getElementById('user-name').textContent = savedUserName; document.getElementById('user-name-input').value = savedUserName; } } catch (e) { tasks = []; categories = []; workspaces = []; } } // ===== Streak ===== function loadStreak() { const lastDate = localStorage.getItem('lastCompleteDate'); const today = new Date().toISOString().split('T')[0]; if (lastDate === today) { streakCount = parseInt(localStorage.getItem('streakCount')) || 0; } else if (lastDate === getYesterday()) { streakCount = parseInt(localStorage.getItem('streakCount')) || 0; } else { streakCount = 0; localStorage.setItem('streakCount', '0'); } updateStreakDisplay(); } function getYesterday() { const d = new Date(); d.setDate(d.getDate() - 1); return d.toISOString().split('T')[0]; } function updateStreakDisplay() { const existing = document.querySelector('.streak-badge'); if (existing) existing.remove(); if (streakCount > 0) { const badge = document.createElement('span'); badge.className = 'streak-badge'; badge.innerHTML = ` ${streakCount} day${streakCount > 1 ? 's' : ''}`; document.querySelector('.user-info h3').after(badge); } } function checkStreak() { const today = new Date().toISOString().split('T')[0]; const lastDate = localStorage.getItem('lastCompleteDate'); if (lastDate === today) return; if (lastDate === getYesterday()) { streakCount++; } else { streakCount = 1; } localStorage.setItem('lastCompleteDate', today); localStorage.setItem('streakCount', streakCount.toString()); updateStreakDisplay(); } // ===== My Day ===== function addToMyDay(taskId) { const task = tasks.find(t => t.id === taskId); if (!task) return; if (!myDayTasks.includes(taskId)) { myDayTasks.push(taskId); localStorage.setItem('myDayTasks', JSON.stringify(myDayTasks)); showToast('Added to My Day', 'success'); } } function loadMyDay() { try { myDayTasks = JSON.parse(localStorage.getItem('myDayTasks')) || []; } catch { myDayTasks = []; } } // ===== Event Listeners ===== function initializeEventListeners() { document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', (e) => { switchView(e.currentTarget.dataset.view); }); }); document.getElementById('add-task-btn').addEventListener('click', () => openTaskModal()); document.getElementById('close-modal').addEventListener('click', closeTaskModal); document.getElementById('cancel-btn').addEventListener('click', closeTaskModal); document.getElementById('task-form').addEventListener('submit', saveTask); document.getElementById('task-recurring').addEventListener('change', (e) => { document.getElementById('recurring-options').style.display = e.target.checked ? 'block' : 'none'; }); document.getElementById('add-category-btn').addEventListener('click', openCategoryModal); document.getElementById('add-workspace-btn').addEventListener('click', openWorkspaceModal); document.getElementById('close-workspace-modal').addEventListener('click', closeWorkspaceModal); document.getElementById('cancel-workspace-btn').addEventListener('click', closeWorkspaceModal); document.getElementById('workspace-form').addEventListener('submit', saveWorkspace); document.getElementById('close-category-modal').addEventListener('click', closeCategoryModal); document.getElementById('cancel-category-btn').addEventListener('click', closeCategoryModal); document.getElementById('category-form').addEventListener('submit', saveCategory); document.querySelectorAll('.color-option').forEach(option => { option.addEventListener('click', function() { document.querySelectorAll('.color-option').forEach(o => o.classList.remove('active')); this.classList.add('active'); }); }); document.getElementById('settings-btn').addEventListener('click', openSettingsModal); document.getElementById('close-settings-modal').addEventListener('click', closeSettingsModal); document.getElementById('pomodoro-toggle-btn').addEventListener('click', togglePomodoroPanel); document.getElementById('pomodoro-start').addEventListener('click', togglePomodoro); document.getElementById('pomodoro-reset').addEventListener('click', resetPomodoro); document.getElementById('pomodoro-skip').addEventListener('click', skipPomodoro); document.getElementById('focus-mode-btn').addEventListener('click', toggleFocusMode); document.getElementById('template-btn').addEventListener('click', openTemplateModal); document.getElementById('close-template-modal').addEventListener('click', closeTemplateModal); document.getElementById('smart-filter-btn').addEventListener('click', cycleSmartFilter); document.getElementById('stats-btn').addEventListener('click', openStatsModal); document.getElementById('close-stats-modal').addEventListener('click', closeStatsModal); document.getElementById('shortcuts-btn').addEventListener('click', openShortcutsModal); document.getElementById('close-shortcuts-modal').addEventListener('click', closeShortcutsModal); document.getElementById('user-name-input').addEventListener('change', (e) => { const name = e.target.value.trim() || 'Welcome Back!'; document.getElementById('user-name').textContent = name; localStorage.setItem('userName', name); }); document.getElementById('search-input').addEventListener('input', handleSearch); document.getElementById('quick-add-form').addEventListener('submit', handleQuickAddSubmit); document.getElementById('complete-visible-btn').addEventListener('click', completeVisibleTasks); document.getElementById('clear-completed-btn').addEventListener('click', clearCompletedTasks); document.getElementById('filter-btn').addEventListener('click', toggleFilterPanel); document.getElementById('view-toggle').addEventListener('click', toggleViewMode); document.getElementById('calendar-toggle').addEventListener('click', toggleCalendarView); document.getElementById('calendar-prev').addEventListener('click', () => { calendarMonth = new Date(calendarMonth.getFullYear(), calendarMonth.getMonth() - 1, 1); renderCalendar(); }); document.getElementById('calendar-next').addEventListener('click', () => { calendarMonth = new Date(calendarMonth.getFullYear(), calendarMonth.getMonth() + 1, 1); renderCalendar(); }); document.querySelectorAll('.filter-option').forEach(option => { option.addEventListener('click', handleFilter); }); document.querySelectorAll('.sort-option').forEach(option => { option.addEventListener('click', handleSort); }); document.getElementById('add-subtask-btn').addEventListener('click', (e) => { e.preventDefault(); addSubtaskInput(); }); document.getElementById('theme-toggle').addEventListener('click', toggleDarkMode); document.getElementById('dark-mode-toggle').addEventListener('change', toggleDarkMode); document.getElementById('animations-toggle').addEventListener('change', (e) => { localStorage.setItem('animationsEnabled', e.target.checked ? 'true' : 'false'); applySettings(); }); document.getElementById('notifications-toggle').addEventListener('change', (e) => { localStorage.setItem('notificationsEnabled', e.target.checked ? 'true' : 'false'); if (e.target.checked) checkNotifications(); }); document.getElementById('sound-toggle').addEventListener('change', (e) => { localStorage.setItem('soundEnabled', e.target.checked ? 'true' : 'false'); }); document.getElementById('export-data-btn').addEventListener('click', exportData); document.getElementById('import-data-btn').addEventListener('click', () => { document.getElementById('import-file-input').click(); }); document.getElementById('import-file-input').addEventListener('change', importData); document.getElementById('clear-data-btn').addEventListener('click', clearAllData); document.getElementById('sidebar-toggle').addEventListener('click', () => { document.getElementById('sidebar').classList.toggle('active'); }); document.querySelectorAll('.modal').forEach(modal => { modal.addEventListener('click', (e) => { if (e.target === modal) modal.classList.remove('active'); }); }); } // ===== Pomodoro ===== function togglePomodoroPanel() { const panel = document.getElementById('pomodoro-container'); panel.classList.toggle('active'); } function togglePomodoro() { if (pomodoroRunning) { clearInterval(pomodoroTimer); pomodoroRunning = false; document.getElementById('pomodoro-start').innerHTML = ''; document.getElementById('pomodoro-start').classList.remove('active'); } else { pomodoroRunning = true; document.getElementById('pomodoro-start').innerHTML = ''; document.getElementById('pomodoro-start').classList.add('active'); pomodoroTimer = setInterval(() => { pomodoroTime--; updatePomodoroDisplay(); if (pomodoroTime <= 0) { clearInterval(pomodoroTimer); pomodoroRunning = false; document.getElementById('pomodoro-start').innerHTML = ''; document.getElementById('pomodoro-start').classList.remove('active'); pomodoroComplete(); } }, 1000); } } function resetPomodoro() { clearInterval(pomodoroTimer); pomodoroRunning = false; pomodoroPhase = 'focus'; pomodoroTime = 25 * 60; document.getElementById('pomodoro-start').innerHTML = ''; document.getElementById('pomodoro-start').classList.remove('active'); document.getElementById('pomodoro-status').textContent = 'Focus'; updatePomodoroDisplay(); } function skipPomodoro() { clearInterval(pomodoroTimer); pomodoroRunning = false; pomodoroComplete(); } function pomodoroComplete() { if (pomodoroPhase === 'focus') { pomodoroSessions++; pomodoroTodayMinutes += 25; saveToLocalStorage(); document.getElementById('pomodoro-sessions').textContent = pomodoroSessions; document.getElementById('pomodoro-today-min').textContent = pomodoroTodayMinutes; if (pomodoroSessions % 4 === 0) { pomodoroPhase = 'long-break'; pomodoroTime = 15 * 60; showToast('Long break time! Great work! 🎉', 'success'); triggerConfetti(); } else { pomodoroPhase = 'short-break'; pomodoroTime = 5 * 60; showToast('Good job! Take a short break ☕', 'success'); } document.getElementById('pomodoro-status').textContent = pomodoroPhase === 'short-break' ? 'Short Break' : 'Long Break'; } else { pomodoroPhase = 'focus'; pomodoroTime = 25 * 60; document.getElementById('pomodoro-status').textContent = 'Focus'; showToast('Break over! Time to focus 🎯', 'info'); } updatePomodoroDisplay(); } function updatePomodoroDisplay() { const mins = Math.floor(pomodoroTime / 60); const secs = pomodoroTime % 60; document.getElementById('pomodoro-timer').textContent = `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; document.getElementById('pomodoro-sessions').textContent = pomodoroSessions; document.getElementById('pomodoro-today-min').textContent = pomodoroTodayMinutes; } // ===== Stats ===== function openStatsModal() { const total = tasks.length; const completed = tasks.filter(t => t.completed).length; const pending = total - completed; document.getElementById('stats-total').textContent = total; document.getElementById('stats-completed').textContent = completed; document.getElementById('stats-pending').textContent = pending; document.getElementById('stats-streak').textContent = streakCount; renderChart(); document.getElementById('stats-modal').classList.add('active'); } function closeStatsModal() { document.getElementById('stats-modal').classList.remove('active'); } function renderChart() { const container = document.getElementById('chart-bars'); container.innerHTML = ''; const days = []; for (let i = 6; i >= 0; i--) { const d = new Date(); d.setDate(d.getDate() - i); days.push(d.toISOString().split('T')[0]); } const maxVal = Math.max(1, ...days.map(day => tasks.filter(t => { if (!t.createdAt) return false; const taskDay = t.createdAt.split('T')[0]; return taskDay === day && t.completed; }).length )); days.forEach(day => { const count = tasks.filter(t => { if (!t.createdAt) return false; return t.createdAt.split('T')[0] === day && t.completed; }).length; const bar = document.createElement('div'); bar.className = 'chart-bar'; const pct = (count / maxVal) * 100; bar.style.height = `${Math.max(pct, 4)}%`; const dateObj = new Date(day + 'T00:00:00'); const label = dateObj.toLocaleDateString('en-US', { weekday: 'short' }); bar.innerHTML = ` ${count} ${label} `; container.appendChild(bar); }); } // ===== Shortcuts Modal ===== function openShortcutsModal() { document.getElementById('shortcuts-modal').classList.add('active'); } function closeShortcutsModal() { document.getElementById('shortcuts-modal').classList.remove('active'); } // ===== View Management ===== function switchView(view) { currentView = view; currentFilter.category = null; currentFilter.workspace = null; currentFilter.date = null; document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active')); document.querySelector(`[data-view="${view}"]`).classList.add('active'); document.querySelectorAll('.category-item').forEach(item => item.classList.remove('active')); const titles = { all: { title: 'All Tasks', subtitle: 'Manage your tasks efficiently' }, myday: { title: 'My Day', subtitle: 'Tasks you want to focus on today' }, today: { title: 'Today', subtitle: 'Tasks due today' }, upcoming: { title: 'Upcoming', subtitle: 'Tasks coming up' }, overdue: { title: 'Overdue', subtitle: 'Tasks that need attention' }, completed: { title: 'Completed', subtitle: 'Finished tasks' }, important: { title: 'Important', subtitle: 'High priority tasks' } }; document.getElementById('view-title').textContent = titles[view].title; document.getElementById('view-subtitle').textContent = titles[view].subtitle; renderTasks(); updateProgress(); } function toggleViewMode() { viewMode = viewMode === 'list' ? 'grid' : 'list'; document.querySelector('#view-toggle i').className = viewMode === 'list' ? 'fas fa-th' : 'fas fa-list'; localStorage.setItem('viewMode', viewMode); renderTasks(); } // ===== Quick Actions ===== function setQuickAddDefaults() { const dateInput = document.getElementById('quick-add-date'); if (dateInput) dateInput.value = new Date().toISOString().split('T')[0]; } function handleQuickAddSubmit(e) { e.preventDefault(); const title = document.getElementById('quick-add-title').value.trim(); if (!title) { showToast('Please enter a task title', 'error'); return; } const parsedTask = parseNaturalLanguageTask(title); const newTask = { id: Date.now(), title: parsedTask.title, description: parsedTask.description, dueDate: parsedTask.dueDate || document.getElementById('quick-add-date').value, dueTime: parsedTask.dueTime || '', priority: parsedTask.priority || 'medium', category: parsedTask.category || '', workspace: '', tags: parsedTask.tags || [], important: parsedTask.important || false, recurring: false, recurrence: null, subtasks: [], completed: false, createdAt: new Date().toISOString() }; tasks.unshift(newTask); saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); showToast('Task added!', 'success'); playSound('create'); document.getElementById('quick-add-form').reset(); setQuickAddDefaults(); } function parseNaturalLanguageTask(input) { const text = input.toLowerCase(); const result = { title: input.trim(), description: '', dueDate: '', dueTime: '', priority: '', category: '', tags: [], important: false }; if (text.includes('urgent') || text.includes('asap')) { result.priority = 'high'; result.important = true; } else if (text.includes('later')) { result.priority = 'low'; } else if (text.includes('important')) { result.priority = 'high'; result.important = true; } const today = new Date(); const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1); const nextWeek = new Date(today); nextWeek.setDate(today.getDate() + 7); if (text.includes('today')) result.dueDate = today.toISOString().split('T')[0]; else if (text.includes('tomorrow')) result.dueDate = tomorrow.toISOString().split('T')[0]; else if (text.includes('week')) result.dueDate = nextWeek.toISOString().split('T')[0]; if (text.includes('work')) { result.category = 'work'; result.tags.push('work'); } else if (text.includes('personal')) { result.category = 'personal'; result.tags.push('personal'); } const match = input.match(/@([a-zA-Z0-9-_]+)/); if (match) result.tags.push(match[1]); return result; } function completeVisibleTasks() { const visibleTasks = getFilteredTasks().filter(task => !task.completed); if (visibleTasks.length === 0) { showToast('No visible tasks to complete', 'info'); return; } visibleTasks.forEach(task => { task.completed = true; }); saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); showToast(`Completed ${visibleTasks.length} task${visibleTasks.length !== 1 ? 's' : ''}`, 'success'); } function toggleFocusMode() { focusModeEnabled = !focusModeEnabled; document.body.classList.toggle('focus-mode', focusModeEnabled); document.getElementById('focus-mode-btn').innerHTML = focusModeEnabled ? ' Focus On' : ' Focus Mode'; showToast(focusModeEnabled ? 'Focus mode enabled' : 'Focus mode disabled', 'info'); } function openTemplateModal() { const list = document.getElementById('template-list'); list.innerHTML = ''; const templates = [ { name: 'Deep Work', tasks: ['Block 90 minutes for focused work', 'Close all distractions', 'Review progress'] }, { name: 'Weekly Review', tasks: ['Review completed tasks', 'Plan next week', 'Update notes'] }, { name: 'Trip Prep', tasks: ['Pack passport', 'Check travel documents', 'Confirm bookings'] }, { name: 'Daily Standup', tasks: ['What I did yesterday', 'What I will do today', 'Any blockers'] } ]; templates.forEach(template => { const card = document.createElement('div'); card.className = 'template-card'; card.innerHTML = `${template.name}
${template.tasks.join('
')}
`; list.appendChild(card); }); list.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', () => { const tasksToAdd = JSON.parse(btn.dataset.template); tasksToAdd.forEach(taskText => { const parsed = parseNaturalLanguageTask(taskText); tasks.unshift({ id: Date.now() + Math.floor(Math.random() * 9999), title: parsed.title, description: parsed.description, dueDate: parsed.dueDate || '', dueTime: parsed.dueTime || '', priority: parsed.priority || 'medium', category: parsed.category || '', workspace: '', reminder: '', tags: parsed.tags || [], important: parsed.important || false, recurring: false, recurrence: null, subtasks: [], completed: false, createdAt: new Date().toISOString() }); }); saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); closeTemplateModal(); showToast('Template applied!', 'success'); }); }); document.getElementById('template-modal').classList.add('active'); } function closeTemplateModal() { document.getElementById('template-modal').classList.remove('active'); } function cycleSmartFilter() { const filters = ['all', 'high', 'today', 'important']; const index = filters.indexOf(smartFilter); smartFilter = filters[(index + 1) % filters.length]; showToast(`Smart filter: ${smartFilter}`, 'info'); renderTasks(); } function clearCompletedTasks() { const completedCount = tasks.filter(task => task.completed).length; if (completedCount === 0) { showToast('No completed tasks to clear', 'info'); return; } tasks = tasks.filter(task => !task.completed); saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); showToast('Completed tasks cleared', 'success'); } function updateInsights() { const today = new Date().toISOString().split('T')[0]; const activeTasks = tasks.filter(task => !task.completed).length; const overdueTasks = tasks.filter(task => task.dueDate && task.dueDate < today && !task.completed).length; const dueSoonTasks = tasks.filter(task => { if (!task.dueDate || task.completed) return false; const diffDays = Math.round((new Date(`${task.dueDate}T00:00:00`) - new Date(`${today}T00:00:00`)) / (86400000)); return diffDays >= 0 && diffDays <= 2; }).length; document.getElementById('insight-active').textContent = `${activeTasks} task${activeTasks !== 1 ? 's' : ''}`; document.getElementById('insight-overdue').textContent = `${overdueTasks} task${overdueTasks !== 1 ? 's' : ''}`; document.getElementById('insight-soon').textContent = `${dueSoonTasks} task${dueSoonTasks !== 1 ? 's' : ''}`; } function getTaskDueState(task) { if (task.completed || !task.dueDate) return { className: '', label: '' }; const today = new Date(); const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate()); const dueDate = new Date(`${task.dueDate}T00:00:00`); const diffDays = Math.round((dueDate - todayStart) / 86400000); if (diffDays < 0) return { className: 'overdue', label: 'Overdue' }; if (diffDays <= 2) return { className: 'due-soon', label: diffDays === 0 ? 'Due today' : `${diffDays} day${diffDays === 1 ? '' : 's'} left` }; return { className: '', label: '' }; } // ===== Task Management ===== function openTaskModal(taskId = null) { const modal = document.getElementById('task-modal'); const form = document.getElementById('task-form'); const modalTitle = document.getElementById('modal-title'); form.reset(); document.getElementById('subtasks-container').innerHTML = ''; document.getElementById('recurring-options').style.display = 'none'; if (taskId) { editingTaskId = taskId; const task = tasks.find(t => t.id === taskId); if (!task) return; modalTitle.textContent = 'Edit Task'; document.getElementById('task-title').value = task.title; document.getElementById('task-description').value = task.description || ''; document.getElementById('task-date').value = task.dueDate || ''; document.getElementById('task-time').value = task.dueTime || ''; document.getElementById('task-priority').value = task.priority; document.getElementById('task-category').value = task.category || ''; document.getElementById('task-workspace').value = task.workspace || ''; document.getElementById('task-reminder').value = task.reminder || ''; document.getElementById('task-tags').value = task.tags ? task.tags.join(', ') : ''; document.getElementById('task-important').checked = task.important || false; document.getElementById('task-recurring').checked = task.recurring || false; if (task.recurring) { document.getElementById('recurring-options').style.display = 'block'; document.getElementById('task-recurrence').value = task.recurrence || 'daily'; } if (task.subtasks && task.subtasks.length > 0) { task.subtasks.forEach(subtask => addSubtaskInput(subtask.text)); } } else { editingTaskId = null; modalTitle.textContent = 'Add New Task'; } updateCategorySelect(); updateWorkspaceSelect(); modal.classList.add('active'); } function closeTaskModal() { document.getElementById('task-modal').classList.remove('active'); editingTaskId = null; } function saveTask(e) { e.preventDefault(); const title = document.getElementById('task-title').value.trim(); if (!title) { showToast('Please enter a task title', 'error'); return; } const taskData = { title, description: document.getElementById('task-description').value.trim(), dueDate: document.getElementById('task-date').value, dueTime: document.getElementById('task-time').value, priority: document.getElementById('task-priority').value, category: document.getElementById('task-category').value, workspace: document.getElementById('task-workspace').value, reminder: document.getElementById('task-reminder').value, tags: document.getElementById('task-tags').value.trim().split(',').map(t => t.trim()).filter(Boolean), important: document.getElementById('task-important').checked, recurring: document.getElementById('task-recurring').checked, recurrence: document.getElementById('task-recurring').checked ? document.getElementById('task-recurrence').value : null, subtasks: [], completed: false }; document.querySelectorAll('.subtask-item input[type="text"]').forEach(input => { if (input.value.trim()) { taskData.subtasks.push({ id: Date.now() + Math.floor(Math.random() * 9999), text: input.value.trim(), completed: false }); } }); if (editingTaskId) { const taskIndex = tasks.findIndex(t => t.id === editingTaskId); if (taskIndex !== -1) { tasks[taskIndex] = { ...tasks[taskIndex], ...taskData }; showToast('Task updated!', 'success'); } } else { tasks.unshift({ id: Date.now(), createdAt: new Date().toISOString(), ...taskData }); showToast('Task created!', 'success'); playSound('create'); } saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); closeTaskModal(); } function deleteTask(taskId) { const task = tasks.find(t => t.id === taskId); if (!task) return; lastDeletedTask = { task: { ...task }, index: tasks.findIndex(t => t.id === taskId) }; tasks = tasks.filter(t => t.id !== taskId); saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); showToast('Task deleted!', 'success', 'Undo', restoreLastDeletedTask); playSound('delete'); } function restoreLastDeletedTask() { if (!lastDeletedTask) return; const { task, index } = lastDeletedTask; if (index >= 0) tasks.splice(index, 0, task); else tasks.unshift(task); lastDeletedTask = null; saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); showToast('Task restored!', 'info'); } function toggleTaskComplete(taskId) { const task = tasks.find(t => t.id === taskId); if (!task) return; task.completed = !task.completed; if (task.completed) { showToast('Task completed! 🎉', 'success'); playSound('complete'); triggerConfetti(); checkStreak(); if (task.recurring && task.recurrence) createRecurringTask(task); } else { showToast('Task reopened!', 'info'); } saveToLocalStorage(); renderTasks(); updateCounts(); updateProgress(); updateInsights(); renderCategories(); updateUserStats(); } function createRecurringTask(originalTask) { const newTask = { ...originalTask }; newTask.id = Date.now(); newTask.completed = false; newTask.createdAt = new Date().toISOString(); if (newTask.dueDate) { const currentDate = new Date(newTask.dueDate); if (newTask.recurrence === 'daily') currentDate.setDate(currentDate.getDate() + 1); else if (newTask.recurrence === 'weekly') currentDate.setDate(currentDate.getDate() + 7); else if (newTask.recurrence === 'monthly') currentDate.setMonth(currentDate.getMonth() + 1); newTask.dueDate = currentDate.toISOString().split('T')[0]; } tasks.unshift(newTask); saveToLocalStorage(); } function toggleTaskImportant(taskId) { const task = tasks.find(t => t.id === taskId); if (!task) return; task.important = !task.important; saveToLocalStorage(); renderTasks(); updateCounts(); updateInsights(); showToast(task.important ? 'Marked as important!' : 'Removed from important!', 'info'); } function toggleSubtask(taskId, subtaskId) { const task = tasks.find(t => t.id === taskId); if (!task || !task.subtasks) return; const subtask = task.subtasks.find(s => s.id === subtaskId); if (!subtask) return; subtask.completed = !subtask.completed; saveToLocalStorage(); renderTasks(); } // ===== Render ===== function renderTasks() { const container = document.getElementById('tasks-container'); const emptyState = document.getElementById('empty-state'); if (!container || !emptyState) return; container.classList.remove('list-view', 'grid-view'); container.classList.add(viewMode === 'grid' ? 'grid-view' : 'list-view'); let filteredTasks = getFilteredTasks(); if (filteredTasks.length === 0) { emptyState.style.display = 'flex'; container.querySelectorAll('.task-card').forEach(card => card.remove()); if (sortableInstance) { sortableInstance.destroy(); sortableInstance = null; } return; } emptyState.style.display = 'none'; container.innerHTML = ''; filteredTasks.forEach(task => { container.appendChild(createTaskCard(task)); }); if (currentView === 'all' && !sortableInstance) { sortableInstance = Sortable.create(container, { animation: 200, ghostClass: 'dragging', onEnd: function(evt) { const movedTask = filteredTasks[evt.oldIndex]; filteredTasks.splice(evt.oldIndex, 1); filteredTasks.splice(evt.newIndex, 0, movedTask); const allTaskIds = filteredTasks.map(t => t.id); tasks.sort((a, b) => { const ai = allTaskIds.indexOf(a.id); const bi = allTaskIds.indexOf(b.id); if (ai === -1) return 1; if (bi === -1) return -1; return ai - bi; }); saveToLocalStorage(); } }); } else if (currentView !== 'all' && sortableInstance) { sortableInstance.destroy(); sortableInstance = null; } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function createTaskCard(task) { const card = document.createElement('div'); const dueState = getTaskDueState(task); card.className = `task-card ${task.completed ? 'completed' : ''} ${dueState.className}`; const category = categories.find(c => c.id === task.category); const workspace = workspaces.find(w => w.id === task.workspace); const categoryBadge = category ? `${escapeHtml(category.name)}` : ''; const tagsHtml = task.tags && task.tags.length > 0 ? `
${task.tags.map(tag => `${escapeHtml(tag)}`).join('')}
` : ''; const dueDateHtml = task.dueDate ? `
${formatDate(task.dueDate)}
` : ''; const dueTimeHtml = task.dueTime ? `
${task.dueTime}
` : ''; const workspaceBadge = workspace ? `
${escapeHtml(workspace.name)}
` : ''; const recurringBadge = task.recurring ? `
${task.recurrence}
` : ''; const dueStateBadge = dueState.label ? `
${escapeHtml(dueState.label)}
` : ''; let subtasksHtml = ''; if (task.subtasks && task.subtasks.length > 0) { const completedSubtasks = task.subtasks.filter(s => s.completed).length; const subtasksItems = task.subtasks.map(subtask => { const checked = subtask.completed ? 'checked' : ''; return `
${escapeHtml(subtask.text)}
`; }).join(''); subtasksHtml = `
${completedSubtasks}/${task.subtasks.length} subtasks
${subtasksItems}
`; } const isMyDay = myDayTasks.includes(task.id); card.innerHTML = `

${escapeHtml(task.title)}

${task.description ? `

${escapeHtml(task.description)}

` : ''}
${task.priority} ${categoryBadge} ${dueDateHtml} ${dueTimeHtml} ${workspaceBadge} ${recurringBadge} ${dueStateBadge} ${isMyDay ? ' My Day' : ''}
${tagsHtml} ${subtasksHtml}
`; card.querySelector('.task-checkbox').addEventListener('click', function() { toggleTaskComplete(Number(this.dataset.taskId)); }); card.querySelectorAll('.task-action-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const action = btn.dataset.action; const taskId = Number(btn.dataset.taskId); if (action === 'important') toggleTaskImportant(taskId); else if (action === 'edit') openTaskModal(taskId); else if (action === 'delete') deleteTask(taskId); else if (action === 'myday') addToMyDay(taskId); }); }); card.querySelectorAll('.subtask-checkbox').forEach(cb => { cb.addEventListener('change', (e) => { toggleSubtask(Number(e.target.dataset.taskId), parseFloat(e.target.dataset.subtaskId)); }); }); return card; } function getFilteredTasks() { let filtered = [...tasks]; const today = new Date().toISOString().split('T')[0]; if (smartFilter === 'high') filtered = filtered.filter(task => task.priority === 'high' || task.important); else if (smartFilter === 'today') filtered = filtered.filter(task => task.dueDate === today || !task.dueDate); else if (smartFilter === 'important') filtered = filtered.filter(task => task.important); loadMyDay(); switch (currentView) { case 'myday': filtered = filtered.filter(t => myDayTasks.includes(t.id)); break; case 'today': filtered = filtered.filter(t => t.dueDate === today && !t.completed); break; case 'upcoming': filtered = filtered.filter(t => t.dueDate && t.dueDate > today && !t.completed); break; case 'overdue': filtered = filtered.filter(t => t.dueDate && t.dueDate < today && !t.completed); break; case 'completed': filtered = filtered.filter(t => t.completed); break; case 'important': filtered = filtered.filter(t => t.important && !t.completed); break; } if (currentFilter.priority !== 'all') filtered = filtered.filter(t => t.priority === currentFilter.priority); if (currentFilter.category) filtered = filtered.filter(t => t.category === currentFilter.category); if (currentFilter.workspace) filtered = filtered.filter(t => t.workspace === currentFilter.workspace); if (currentFilter.date) filtered = filtered.filter(t => t.dueDate === currentFilter.date); if (currentFilter.search) { const search = currentFilter.search.toLowerCase(); filtered = filtered.filter(t => t.title.toLowerCase().includes(search) || (t.description && t.description.toLowerCase().includes(search)) || (t.tags && t.tags.some(tag => tag.toLowerCase().includes(search))) ); } filtered.sort((a, b) => { switch (currentSort) { case 'date-desc': return new Date(b.createdAt) - new Date(a.createdAt); case 'date-asc': return new Date(a.createdAt) - new Date(b.createdAt); case 'priority': return ({ high: 3, medium: 2, low: 1 })[b.priority] - ({ high: 3, medium: 2, low: 1 })[a.priority]; case 'alphabetical': return a.title.localeCompare(b.title); case 'duedate': if (!a.dueDate) return 1; if (!b.dueDate) return -1; return new Date(a.dueDate) - new Date(b.dueDate); default: return 0; } }); return filtered; } // ===== Categories ===== function openCategoryModal() { document.getElementById('category-modal').classList.add('active'); } function closeCategoryModal() { document.getElementById('category-modal').classList.remove('active'); document.getElementById('category-form').reset(); document.querySelectorAll('.color-option').forEach(o => o.classList.remove('active')); document.querySelector('.color-option').classList.add('active'); } function saveCategory(e) { e.preventDefault(); const name = document.getElementById('category-name').value.trim(); if (!name) { showToast('Please enter a category name', 'error'); return; } categories.push({ id: Date.now().toString(), name, icon: document.getElementById('category-icon').value, color: document.querySelector('.color-option.active').dataset.color }); saveToLocalStorage(); renderCategories(); updateCategorySelect(); closeCategoryModal(); showToast('Category created!', 'success'); } function renderCategories() { const container = document.getElementById('categories-list'); container.innerHTML = ''; categories.forEach(category => { const item = document.createElement('button'); item.className = 'category-item'; item.dataset.categoryId = category.id; item.addEventListener('click', function() { filterByCategory(category.id, this); }); const count = tasks.filter(t => t.category === category.id && !t.completed).length; item.innerHTML = `
${escapeHtml(category.name)}${count}`; container.appendChild(item); }); } function filterByCategory(categoryId, element) { currentFilter.category = categoryId; document.querySelectorAll('.category-item').forEach(item => item.classList.remove('active')); if (element) element.classList.add('active'); renderTasks(); } function renderWorkspaces() { const container = document.getElementById('workspaces-list'); container.innerHTML = ''; workspaces.forEach(workspace => { const item = document.createElement('button'); item.className = 'category-item'; item.innerHTML = `${escapeHtml(workspace.name)}${tasks.filter(t => t.workspace === workspace.id).length}`; item.addEventListener('click', () => { currentFilter.workspace = workspace.id; currentFilter.category = null; document.querySelectorAll('.category-item').forEach(el => el.classList.remove('active')); renderTasks(); }); container.appendChild(item); }); } function openWorkspaceModal() { document.getElementById('workspace-modal').classList.add('active'); } function closeWorkspaceModal() { document.getElementById('workspace-modal').classList.remove('active'); document.getElementById('workspace-form').reset(); } function saveWorkspace(e) { e.preventDefault(); const name = document.getElementById('workspace-name').value.trim(); if (!name) return; workspaces.push({ id: Date.now().toString(), name }); saveToLocalStorage(); renderWorkspaces(); updateWorkspaceSelect(); closeWorkspaceModal(); showToast('Workspace created!', 'success'); } function updateWorkspaceSelect() { const select = document.getElementById('task-workspace'); const currentValue = select.value; select.innerHTML = ''; workspaces.forEach(w => { const o = document.createElement('option'); o.value = w.id; o.textContent = w.name; select.appendChild(o); }); if (currentValue) select.value = currentValue; } function updateCategorySelect() { const select = document.getElementById('task-category'); const currentValue = select.value; select.innerHTML = ''; categories.forEach(c => { const o = document.createElement('option'); o.value = c.id; o.textContent = c.name; select.appendChild(o); }); if (currentValue) select.value = currentValue; } // ===== Filter & Sort ===== function toggleFilterPanel() { document.getElementById('filter-panel').classList.toggle('active'); } function handleFilter(e) { const filterType = e.currentTarget.dataset.filter; const filterValue = e.currentTarget.dataset.value; currentFilter[filterType] = filterValue; e.currentTarget.parentElement.querySelectorAll('.filter-option').forEach(o => o.classList.remove('active')); e.currentTarget.classList.add('active'); renderTasks(); } function handleSort(e) { currentSort = e.currentTarget.dataset.sort; document.querySelectorAll('.sort-option').forEach(o => o.classList.remove('active')); e.currentTarget.classList.add('active'); renderTasks(); } function handleSearch(e) { currentFilter.search = e.target.value.trim(); renderTasks(); } // ===== Subtasks ===== function addSubtaskInput(subtaskText = '') { const container = document.getElementById('subtasks-container'); const item = document.createElement('div'); item.className = 'subtask-item'; const input = document.createElement('input'); input.type = 'text'; input.placeholder = 'Enter subtask'; input.value = subtaskText; const removeBtn = document.createElement('button'); removeBtn.type = 'button'; removeBtn.innerHTML = ''; removeBtn.addEventListener('click', () => item.remove()); item.appendChild(input); item.appendChild(removeBtn); container.appendChild(item); } // ===== Settings ===== function openSettingsModal() { document.getElementById('settings-modal').classList.add('active'); } function closeSettingsModal() { document.getElementById('settings-modal').classList.remove('active'); } function toggleDarkMode() { document.body.classList.toggle('dark-mode'); const isDark = document.body.classList.contains('dark-mode'); localStorage.setItem('darkMode', isDark ? 'enabled' : 'disabled'); document.getElementById('dark-mode-toggle').checked = isDark; document.querySelector('#theme-toggle i').className = isDark ? 'fas fa-sun' : 'fas fa-moon'; } function applySettings() { const animationsEnabled = localStorage.getItem('animationsEnabled') !== 'false'; const notificationsEnabled = localStorage.getItem('notificationsEnabled') !== 'false'; const soundEnabled = localStorage.getItem('soundEnabled') !== 'false'; const savedViewMode = localStorage.getItem('viewMode'); document.body.classList.toggle('no-animations', !animationsEnabled); document.getElementById('animations-toggle').checked = animationsEnabled; document.getElementById('notifications-toggle').checked = notificationsEnabled; document.getElementById('sound-toggle').checked = soundEnabled; if (savedViewMode === 'grid' || savedViewMode === 'list') { viewMode = savedViewMode; const icon = document.querySelector('#view-toggle i'); if (icon) icon.className = viewMode === 'list' ? 'fas fa-th' : 'fas fa-list'; } } function checkDarkMode() { if (localStorage.getItem('darkMode') === 'enabled') { document.body.classList.add('dark-mode'); document.getElementById('dark-mode-toggle').checked = true; document.querySelector('#theme-toggle i').className = 'fas fa-sun'; } } function exportData() { const data = { tasks, categories, workspaces, exportDate: new Date().toISOString(), version: '3.0' }; const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `taskmaster-backup-${Date.now()}.json`; link.click(); URL.revokeObjectURL(url); showToast('Data exported!', 'success'); } function importData(e) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { try { const data = JSON.parse(event.target.result); if (data.tasks && data.categories) { tasks = data.tasks; categories = data.categories; workspaces = data.workspaces || []; saveToLocalStorage(); renderTasks(); renderCategories(); renderWorkspaces(); updateWorkspaceSelect(); updateCounts(); updateProgress(); updateInsights(); updateUserStats(); showToast('Data imported!', 'success'); } else { showToast('Invalid data format!', 'error'); } } catch (err) { showToast('Error: ' + err.message, 'error'); } }; reader.readAsText(file); e.target.value = ''; } function clearAllData() { if (confirm('Are you sure you want to clear all data? This cannot be undone!')) { tasks = []; categories = []; workspaces = []; saveToLocalStorage(); renderTasks(); renderCategories(); updateCounts(); updateProgress(); updateInsights(); updateUserStats(); showToast('All data cleared!', 'success'); } } // ===== Utility ===== function toggleCalendarView() { calendarViewVisible = !calendarViewVisible; document.getElementById('calendar-view-panel').hidden = !calendarViewVisible; document.getElementById('tasks-container').hidden = calendarViewVisible; if (calendarViewVisible) renderCalendar(); } function renderCalendar() { const grid = document.getElementById('calendar-grid'); const label = document.getElementById('calendar-label'); label.textContent = calendarMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); const year = calendarMonth.getFullYear(); const month = calendarMonth.getMonth(); const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); const cells = []; for (let i = 0; i < firstDay; i++) cells.push('
'); for (let day = 1; day <= daysInMonth; day++) { const dateKey = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; const dayTasks = tasks.filter(task => task.dueDate === dateKey && !task.completed); const isSelected = currentFilter.date === dateKey; cells.push(`
${day}
${dayTasks.length} task${dayTasks.length === 1 ? '' : 's'}
`); } grid.innerHTML = cells.join(''); grid.querySelectorAll('.calendar-cell[data-date]').forEach(cell => { cell.addEventListener('click', () => { currentFilter.date = cell.dataset.date; renderCalendar(); renderTasks(); }); }); } function updateCounts() { const today = new Date().toISOString().split('T')[0]; loadMyDay(); document.getElementById('all-count').textContent = tasks.filter(t => !t.completed).length; document.getElementById('myday-count').textContent = myDayTasks.length; document.getElementById('today-count').textContent = tasks.filter(t => t.dueDate === today && !t.completed).length; document.getElementById('upcoming-count').textContent = tasks.filter(t => t.dueDate && t.dueDate > today && !t.completed).length; document.getElementById('overdue-count').textContent = tasks.filter(t => t.dueDate && t.dueDate < today && !t.completed).length; document.getElementById('completed-count').textContent = tasks.filter(t => t.completed).length; document.getElementById('important-count').textContent = tasks.filter(t => t.important && !t.completed).length; document.getElementById('stat-completed').textContent = tasks.filter(t => t.completed).length; document.getElementById('stat-pending').textContent = tasks.filter(t => !t.completed).length; } function updateProgress() { const today = new Date().toISOString().split('T')[0]; const todayTasks = tasks.filter(t => t.dueDate === today); const completedToday = todayTasks.filter(t => t.completed).length; const totalToday = todayTasks.length; const percentage = totalToday > 0 ? Math.round((completedToday / totalToday) * 100) : 0; document.getElementById('progress-text').textContent = `${percentage}%`; document.getElementById('progress-fill').style.width = `${percentage}%`; } function updateUserStats() { const today = new Date().toISOString().split('T')[0]; const todayTasks = tasks.filter(t => t.dueDate === today && !t.completed).length; document.getElementById('user-stats').textContent = `${todayTasks} task${todayTasks !== 1 ? 's' : ''} today`; } function formatDate(dateString) { const date = new Date(dateString); const today = new Date(); const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); const todayStr = today.toISOString().split('T')[0]; const tomorrowStr = tomorrow.toISOString().split('T')[0]; if (dateString === todayStr) return 'Today'; else if (dateString === tomorrowStr) return 'Tomorrow'; else if (dateString < todayStr) return '⚠️ Overdue'; else return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } function showToast(message, type = 'success', actionLabel = '', actionHandler = null) { const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toast-message'); const toastIcon = toast.querySelector('i'); const toastAction = document.getElementById('toast-action'); toastMessage.textContent = message; if (actionLabel && actionHandler) { toastAction.textContent = actionLabel; toastAction.style.display = 'inline-flex'; toastAction.onclick = actionHandler; } else { toastAction.textContent = ''; toastAction.style.display = 'none'; toastAction.onclick = null; } toastIcon.className = type === 'success' ? 'fas fa-check-circle' : type === 'error' ? 'fas fa-exclamation-circle' : 'fas fa-info-circle'; toast.style.background = type === 'success' ? 'var(--success)' : type === 'error' ? 'var(--danger)' : 'var(--info)'; toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 3500); } function scheduleReminders() { const now = new Date(); tasks.filter(task => task.reminder && !task.completed).forEach(task => { const reminderTime = new Date(task.reminder); if (reminderTime > now) { setTimeout(() => { if (Notification.permission === 'granted') { new Notification('Task Reminder', { body: task.title }); } }, reminderTime - now); } }); } function playSound(type) { if (localStorage.getItem('soundEnabled') === 'false') return; try { const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const osc = audioCtx.createOscillator(); const gain = audioCtx.createGain(); osc.connect(gain); gain.connect(audioCtx.destination); if (type === 'complete') { osc.frequency.value = 800; gain.gain.value = 0.1; osc.start(); osc.stop(audioCtx.currentTime + 0.1); } else if (type === 'create') { osc.frequency.value = 600; gain.gain.value = 0.05; osc.start(); osc.stop(audioCtx.currentTime + 0.05); } else if (type === 'delete') { osc.frequency.value = 400; gain.gain.value = 0.05; osc.start(); osc.stop(audioCtx.currentTime + 0.05); } } catch (e) {} } function triggerConfetti() { const canvas = document.getElementById('confetti-canvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const confetti = []; const count = 60; const colors = ['#6366f1', '#a855f7', '#ec4899', '#f59e0b', '#22c55e', '#06b6d4']; for (let i = 0; i < count; i++) { confetti.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height - canvas.height, r: Math.random() * 6 + 3, d: Math.random() * count, color: colors[Math.floor(Math.random() * colors.length)], tilt: Math.random() * 10 - 10, tiltAngleIncremental: Math.random() * 0.07 + 0.05, tiltAngle: 0 }); } let frameCount = 0; function draw() { frameCount++; ctx.clearRect(0, 0, canvas.width, canvas.height); confetti.forEach((c, i) => { ctx.beginPath(); ctx.lineWidth = c.r / 2; ctx.strokeStyle = c.color; c.tiltAngle += c.tiltAngleIncremental; c.y += (Math.cos(c.d) + 3 + c.r / 2) / 2; c.tilt = Math.sin(c.tiltAngle - i / 3) * 15; ctx.moveTo(c.x + c.tilt + c.r, c.y); ctx.lineTo(c.x + c.tilt, c.y + c.tilt + c.r); ctx.stroke(); if (c.y > canvas.height) confetti.splice(i, 1); }); if (confetti.length > 0 && frameCount < 150) { requestAnimationFrame(draw); } else { ctx.clearRect(0, 0, canvas.width, canvas.height); } } draw(); } function initParticles() { const canvas = document.getElementById('particles-canvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const particles = []; const count = 40; for (let i = 0; i < count; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * 0.5, vy: (Math.random() - 0.5) * 0.5, r: Math.random() * 2 + 1, opacity: Math.random() * 0.5 + 0.1 }); } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); const isDark = document.body.classList.contains('dark-mode'); particles.forEach(p => { p.x += p.vx; p.y += p.vy; if (p.x < 0) p.x = canvas.width; if (p.x > canvas.width) p.x = 0; if (p.y < 0) p.y = canvas.height; if (p.y > canvas.height) p.y = 0; ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); ctx.fillStyle = isDark ? `rgba(255,255,255,${p.opacity * 0.3})` : `rgba(99,102,241,${p.opacity * 0.3})`; ctx.fill(); }); requestAnimationFrame(animate); } animate(); window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); } // ===== Keyboard ===== document.addEventListener('keydown', (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'n') { e.preventDefault(); openTaskModal(); } if ((e.ctrlKey || e.metaKey) && e.key === 'd') { e.preventDefault(); toggleDarkMode(); } if ((e.ctrlKey || e.metaKey) && e.key === 'f') { e.preventDefault(); document.getElementById('search-input').focus(); } if ((e.ctrlKey || e.metaKey) && e.key === 'p') { e.preventDefault(); togglePomodoroPanel(); } if ((e.ctrlKey || e.metaKey) && e.key === 'Shift' && e.key === 'F') { e.preventDefault(); toggleFocusMode(); } if (e.key === 'Escape') { document.querySelectorAll('.modal.active').forEach(modal => modal.classList.remove('active')); } }); // ===== Notifications ===== function checkNotifications() { if (localStorage.getItem('notificationsEnabled') === 'false') return; if (!('Notification' in window)) return; if (Notification.permission === 'granted') { const today = new Date().toISOString().split('T')[0]; const todayTasks = tasks.filter(t => t.dueDate === today && !t.completed); if (todayTasks.length > 0) { new Notification('TaskMaster Pro', { body: `You have ${todayTasks.length} task${todayTasks.length !== 1 ? 's' : ''} due today!` }); } } else if (Notification.permission !== 'denied') { Notification.requestPermission(); } } setTimeout(() => { checkNotifications(); scheduleReminders(); loadMyDay(); }, 2000);