| <!DOCTYPE html> |
| <html lang="es"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>NeuroFlow - Suite de Productividad Estudiantil</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"> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| primary: '#00f0ff', |
| secondary: '#ff00f0', |
| dark: '#0f172a', |
| glass: 'rgba(15, 23, 42, 0.7)', |
| }, |
| animation: { |
| 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', |
| 'float': 'float 6s ease-in-out infinite', |
| }, |
| keyframes: { |
| float: { |
| '0%, 100%': { transform: 'translateY(0)' }, |
| '50%': { transform: 'translateY(-10px)' }, |
| } |
| } |
| } |
| } |
| } |
| </script> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700&family=Inter:wght@300;400;500;600&display=swap'); |
| |
| body { |
| font-family: 'Inter', sans-serif; |
| background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); |
| color: white; |
| min-height: 100vh; |
| overflow-x: hidden; |
| } |
| |
| .tech-font { |
| font-family: 'Orbitron', sans-serif; |
| } |
| |
| .glass-panel { |
| background: rgba(15, 23, 42, 0.5); |
| backdrop-filter: blur(10px); |
| border: 1px solid rgba(255, 255, 255, 0.1); |
| box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.36); |
| } |
| |
| .neon-border { |
| border: 1px solid rgba(0, 240, 255, 0.3); |
| box-shadow: 0 0 10px rgba(0, 240, 255, 0.3), inset 0 0 10px rgba(0, 240, 255, 0.3); |
| } |
| |
| .neon-text { |
| text-shadow: 0 0 5px rgba(0, 240, 255, 0.8); |
| } |
| |
| .progress-ring { |
| transform: rotate(-90deg); |
| } |
| |
| .progress-ring__circle { |
| transition: stroke-dashoffset 0.5s; |
| transform-origin: 50% 50%; |
| } |
| |
| .task-complete { |
| animation: taskComplete 0.5s ease-out; |
| } |
| |
| @keyframes taskComplete { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.1); } |
| 100% { transform: scale(1); } |
| } |
| |
| .coin-reward { |
| animation: coinReward 1s ease-out forwards; |
| } |
| |
| @keyframes coinReward { |
| 0% { transform: translateY(0) scale(1); opacity: 1; } |
| 100% { transform: translateY(-100px) scale(1.5); opacity: 0; } |
| } |
| |
| .quote-transition { |
| animation: fadeIn 1s ease-out; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| </style> |
| </head> |
| <body class="relative"> |
| |
| <div class="fixed top-0 left-0 w-full h-full overflow-hidden z-0"> |
| <div class="absolute top-20 left-10 w-4 h-4 rounded-full bg-primary opacity-20 animate-pulse-slow"></div> |
| <div class="absolute top-1/3 right-20 w-6 h-6 rounded-full bg-secondary opacity-20 animate-pulse-slow"></div> |
| <div class="absolute bottom-1/4 left-1/3 w-8 h-8 rounded-full bg-primary opacity-20 animate-pulse-slow"></div> |
| <div class="absolute bottom-20 right-1/4 w-5 h-5 rounded-full bg-secondary opacity-20 animate-pulse-slow"></div> |
| </div> |
| |
| |
| <div class="container mx-auto px-4 py-8 max-w-6xl relative z-10"> |
| |
| <header class="flex justify-between items-center mb-8"> |
| <div class="flex items-center"> |
| <div class="w-12 h-12 rounded-full bg-gradient-to-br from-primary to-secondary flex items-center justify-center mr-4"> |
| <i class="fas fa-brain text-xl text-white"></i> |
| </div> |
| <h1 class="tech-font text-3xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-secondary"> |
| NeuroFlow |
| </h1> |
| </div> |
| <div class="flex items-center space-x-4"> |
| <div class="glass-panel px-4 py-2 rounded-full flex items-center neon-border"> |
| <i class="fas fa-coins text-yellow-400 mr-2"></i> |
| <span id="brain-coins" class="font-bold">0</span> |
| <span class="ml-1 text-xs opacity-70">NeuroCoins</span> |
| </div> |
| <div class="glass-panel px-4 py-2 rounded-full flex items-center neon-border"> |
| <i class="fas fa-star text-primary mr-2"></i> |
| <span id="xp-points" class="font-bold">0</span> |
| <span class="ml-1 text-xs opacity-70">XP</span> |
| </div> |
| </div> |
| </header> |
| |
| |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> |
| |
| <div class="lg:col-span-2 space-y-6"> |
| |
| <div class="glass-panel rounded-2xl p-6 neon-border"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="tech-font text-xl font-bold text-primary">Temporizador</h2> |
| <div class="flex space-x-2"> |
| <button id="pomodoro-btn" class="px-3 py-1 rounded-full bg-primary text-dark font-medium text-sm">Pomodoro</button> |
| <button id="stopwatch-btn" class="px-3 py-1 rounded-full glass-panel text-white font-medium text-sm">Cronómetro</button> |
| </div> |
| </div> |
| |
| <div class="flex flex-col items-center justify-center"> |
| |
| <div class="relative w-64 h-64 mb-6"> |
| <svg class="w-full h-full" viewBox="0 0 100 100"> |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#1e293b" stroke-width="8"/> |
| <circle id="progress-circle" class="progress-ring__circle" cx="50" cy="50" r="45" fill="none" stroke="url(#gradient)" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="0"/> |
| <defs> |
| <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%"> |
| <stop offset="0%" stop-color="#00f0ff"/> |
| <stop offset="100%" stop-color="#ff00f0"/> |
| </linearGradient> |
| </defs> |
| </svg> |
| <div class="absolute inset-0 flex items-center justify-center flex-col"> |
| <div id="time-display" class="tech-font text-4xl font-bold mb-1">25:00</div> |
| <div id="timer-state" class="text-sm opacity-80">Sesión de Trabajo</div> |
| </div> |
| </div> |
| |
| <div class="flex space-x-4"> |
| <button id="start-timer" class="px-6 py-2 rounded-full bg-primary text-dark font-bold flex items-center"> |
| <i class="fas fa-play mr-2"></i> Iniciar |
| </button> |
| <button id="reset-timer" class="px-6 py-2 rounded-full glass-panel text-white font-bold flex items-center"> |
| <i class="fas fa-redo mr-2"></i> Reiniciar |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="glass-panel rounded-2xl p-6 neon-border"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="tech-font text-xl font-bold text-primary">Lista de Tareas</h2> |
| <div class="text-sm opacity-80"><span id="tasks-completed">0</span> de <span id="tasks-total">0</span> completadas</div> |
| </div> |
| |
| <div class="flex mb-4"> |
| <input id="new-task-input" type="text" placeholder="Añadir nueva tarea..." class="flex-grow bg-transparent border-b border-primary/50 focus:border-primary focus:outline-none py-2 px-1 text-white placeholder-gray-400"> |
| <button id="add-task-btn" class="ml-2 px-4 py-2 bg-primary text-dark rounded-full font-medium"> |
| <i class="fas fa-plus"></i> |
| </button> |
| </div> |
| |
| <div id="task-list" class="space-y-2 max-h-64 overflow-y-auto pr-2"> |
| |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="space-y-6"> |
| |
| <div class="glass-panel rounded-2xl p-6 neon-border h-full"> |
| <h2 class="tech-font text-xl font-bold text-primary mb-6">Enfoque</h2> |
| |
| <div id="quote-display" class="quote-transition mb-6 p-4 rounded-xl bg-gradient-to-br from-dark to-gray-800"> |
| <p class="italic text-lg mb-2">"El experto en cualquier cosa fue una vez un principiante."</p> |
| <p class="text-right text-sm opacity-70">- Helen Hayes</p> |
| </div> |
| |
| <div class="flex justify-between mb-6"> |
| <button id="prev-quote" class="px-3 py-1 rounded-full glass-panel text-white"> |
| <i class="fas fa-chevron-left"></i> |
| </button> |
| <button id="next-quote" class="px-3 py-1 rounded-full glass-panel text-white"> |
| <i class="fas fa-chevron-right"></i> |
| </button> |
| </div> |
| |
| <div class="mb-6"> |
| <h3 class="text-sm font-medium mb-2 opacity-80">Sonidos Ambientales</h3> |
| <div class="grid grid-cols-2 gap-2"> |
| <button class="sound-btn px-3 py-2 rounded-lg glass-panel flex items-center justify-center" data-sound="rain"> |
| <i class="fas fa-cloud-rain mr-2"></i> Lluvia |
| </button> |
| <button class="sound-btn px-3 py-2 rounded-lg glass-panel flex items-center justify-center" data-sound="forest"> |
| <i class="fas fa-tree mr-2"></i> Bosque |
| </button> |
| <button class="sound-btn px-3 py-2 rounded-lg glass-panel flex items-center justify-center" data-sound="coffee"> |
| <i class="fas fa-coffee mr-2"></i> Café |
| </button> |
| <button class="sound-btn px-3 py-2 rounded-lg glass-panel flex items-center justify-center" data-sound="white"> |
| <i class="fas fa-wind mr-2"></i> Ruido Blanco |
| </button> |
| </div> |
| </div> |
| |
| <div class="flex items-center justify-between"> |
| <div> |
| <h3 class="text-sm font-medium mb-1 opacity-80">Volumen</h3> |
| <input type="range" id="volume-control" min="0" max="1" step="0.1" value="0.5" class="w-full"> |
| </div> |
| <button id="toggle-sound" class="px-4 py-2 rounded-full bg-primary text-dark font-medium"> |
| <i class="fas fa-volume-up"></i> |
| </button> |
| </div> |
| </div> |
| |
| |
| <div class="glass-panel rounded-2xl p-6 neon-border"> |
| <h2 class="tech-font text-xl font-bold text-primary mb-4">Estadísticas Diarias</h2> |
| |
| <div class="space-y-4"> |
| <div> |
| <div class="flex justify-between text-sm mb-1"> |
| <span>Sesiones de Enfoque</span> |
| <span id="sessions-count">0</span> |
| </div> |
| <div class="w-full bg-gray-700 rounded-full h-2"> |
| <div id="sessions-bar" class="bg-primary h-2 rounded-full" style="width: 0%"></div> |
| </div> |
| </div> |
| |
| <div> |
| <div class="flex justify-between text-sm mb-1"> |
| <span>Tareas Completadas</span> |
| <span id="tasks-completed-count">0</span> |
| </div> |
| <div class="w-full bg-gray-700 rounded-full h-2"> |
| <div id="tasks-bar" class="bg-secondary h-2 rounded-full" style="width: 0%"></div> |
| </div> |
| </div> |
| |
| <div> |
| <div class="flex justify-between text-sm mb-1"> |
| <span>Nivel de Productividad</span> |
| <span id="productivity-level">0%</span> |
| </div> |
| <div class="w-full bg-gray-700 rounded-full h-2"> |
| <div id="productivity-bar" class="bg-gradient-to-r from-primary to-secondary h-2 rounded-full" style="width: 0%"></div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <footer class="mt-12 text-center text-sm opacity-60"> |
| <p>Creado por Reshman</p> |
| </footer> |
| </div> |
| |
| |
| <audio id="rain-sound" loop src="https://assets.mixkit.co/sfx/preview/mixkit-rain-02-124.mp3"></audio> |
| <audio id="forest-sound" loop src="https://assets.mixkit.co/sfx/preview/mixkit-forest-birds-1603.mp3"></audio> |
| <audio id="coffee-sound" loop src="https://assets.mixkit.co/sfx/preview/mixkit-busy-coffee-shop-123.mp3"></audio> |
| <audio id="white-sound" loop src="https://assets.mixkit.co/sfx/preview/mixkit-wind-1250.mp3"></audio> |
| <audio id="complete-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3"></audio> |
| <audio id="timer-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-alarm-digital-clock-beep-989.mp3"></audio> |
| |
| <script> |
| |
| const state = { |
| timerMode: 'pomodoro', |
| timerRunning: false, |
| timeLeft: 25 * 60, |
| workDuration: 25 * 60, |
| breakDuration: 5 * 60, |
| timerInterval: null, |
| currentQuoteIndex: 0, |
| brainCoins: 0, |
| xpPoints: 0, |
| tasks: [], |
| completedTasks: 0, |
| focusSessions: 0, |
| soundEnabled: false, |
| currentSound: null, |
| volume: 0.5, |
| stopwatchRunning: false, |
| stopwatchTime: 0, |
| stopwatchInterval: null |
| }; |
| |
| |
| const quotes = [ |
| { text: "El experto en cualquier cosa fue una vez un principiante.", author: "Helen Hayes" }, |
| { text: "La productividad nunca es un accidente. Es siempre el resultado de un compromiso con la excelencia, planificación inteligente y esfuerzo enfocado.", author: "Paul J. Meyer" }, |
| { text: "Concentra todos tus pensamientos en el trabajo en curso. Los rayos del sol no queman hasta que se enfocan.", author: "Alexander Graham Bell" }, |
| { text: "El secreto para avanzar es comenzar.", author: "Mark Twain" }, |
| { text: "No tienes que ser grande para empezar, pero tienes que empezar para ser grande.", author: "Zig Ziglar" }, |
| { text: "Enfócate en ser productivo en lugar de estar ocupado.", author: "Tim Ferriss" }, |
| { text: "El futuro depende de lo que hagas hoy.", author: "Mahatma Gandhi" }, |
| { text: "Pequeñas mejoras diarias son la clave para resultados impresionantes a largo plazo.", author: "Robin Sharma" }, |
| { text: "La disciplina es el puente entre metas y logros.", author: "Jim Rohn" }, |
| { text: "No cuentes los días, haz que los días cuenten.", author: "Muhammad Ali" } |
| ]; |
| |
| |
| const elements = { |
| timeDisplay: document.getElementById('time-display'), |
| timerState: document.getElementById('timer-state'), |
| progressCircle: document.getElementById('progress-circle'), |
| pomodoroBtn: document.getElementById('pomodoro-btn'), |
| stopwatchBtn: document.getElementById('stopwatch-btn'), |
| startTimer: document.getElementById('start-timer'), |
| resetTimer: document.getElementById('reset-timer'), |
| newTaskInput: document.getElementById('new-task-input'), |
| addTaskBtn: document.getElementById('add-task-btn'), |
| taskList: document.getElementById('task-list'), |
| tasksCompleted: document.getElementById('tasks-completed'), |
| tasksTotal: document.getElementById('tasks-total'), |
| quoteDisplay: document.getElementById('quote-display'), |
| prevQuote: document.getElementById('prev-quote'), |
| nextQuote: document.getElementById('next-quote'), |
| brainCoins: document.getElementById('brain-coins'), |
| xpPoints: document.getElementById('xp-points'), |
| sessionsCount: document.getElementById('sessions-count'), |
| sessionsBar: document.getElementById('sessions-bar'), |
| tasksCompletedCount: document.getElementById('tasks-completed-count'), |
| tasksBar: document.getElementById('tasks-bar'), |
| productivityLevel: document.getElementById('productivity-level'), |
| productivityBar: document.getElementById('productivity-bar'), |
| soundBtns: document.querySelectorAll('.sound-btn'), |
| volumeControl: document.getElementById('volume-control'), |
| toggleSound: document.getElementById('toggle-sound'), |
| rainSound: document.getElementById('rain-sound'), |
| forestSound: document.getElementById('forest-sound'), |
| coffeeSound: document.getElementById('coffee-sound'), |
| whiteSound: document.getElementById('white-sound'), |
| completeSound: document.getElementById('complete-sound'), |
| timerSound: document.getElementById('timer-sound') |
| }; |
| |
| |
| function init() { |
| displayQuote(); |
| updateTimerDisplay(); |
| updateStats(); |
| loadTasks(); |
| |
| |
| elements.pomodoroBtn.addEventListener('click', () => switchTimerMode('pomodoro')); |
| elements.stopwatchBtn.addEventListener('click', () => switchTimerMode('stopwatch')); |
| elements.startTimer.addEventListener('click', toggleTimer); |
| elements.resetTimer.addEventListener('click', resetTimer); |
| elements.addTaskBtn.addEventListener('click', addTask); |
| elements.newTaskInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') addTask(); |
| }); |
| elements.prevQuote.addEventListener('click', showPreviousQuote); |
| elements.nextQuote.addEventListener('click', showNextQuote); |
| elements.volumeControl.addEventListener('input', updateVolume); |
| elements.toggleSound.addEventListener('click', toggleSound); |
| |
| elements.soundBtns.forEach(btn => { |
| btn.addEventListener('click', () => playSound(btn.dataset.sound)); |
| }); |
| |
| |
| const savedState = localStorage.getItem('neuroFlowState'); |
| if (savedState) { |
| const parsedState = JSON.parse(savedState); |
| Object.assign(state, parsedState); |
| |
| |
| updateBrainCoins(); |
| updateXP(); |
| renderTasks(); |
| updateStats(); |
| |
| if (state.timerMode === 'pomodoro') { |
| switchTimerMode('pomodoro'); |
| } else { |
| switchTimerMode('stopwatch'); |
| } |
| |
| if (state.soundEnabled) { |
| toggleSound(); |
| if (state.currentSound) { |
| playSound(state.currentSound); |
| } |
| } |
| |
| elements.volumeControl.value = state.volume; |
| } |
| } |
| |
| |
| function switchTimerMode(mode) { |
| state.timerMode = mode; |
| |
| if (mode === 'pomodoro') { |
| elements.pomodoroBtn.classList.add('bg-primary', 'text-dark'); |
| elements.pomodoroBtn.classList.remove('glass-panel', 'text-white'); |
| elements.stopwatchBtn.classList.remove('bg-primary', 'text-dark'); |
| elements.stopwatchBtn.classList.add('glass-panel', 'text-white'); |
| |
| elements.timerState.textContent = 'Sesión de Trabajo'; |
| state.timeLeft = state.workDuration; |
| updateTimerDisplay(); |
| |
| |
| if (state.stopwatchRunning) { |
| clearInterval(state.stopwatchInterval); |
| state.stopwatchRunning = false; |
| elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar'; |
| } |
| } else { |
| elements.stopwatchBtn.classList.add('bg-primary', 'text-dark'); |
| elements.stopwatchBtn.classList.remove('glass-panel', 'text-white'); |
| elements.pomodoroBtn.classList.remove('bg-primary', 'text-dark'); |
| elements.pomodoroBtn.classList.add('glass-panel', 'text-white'); |
| |
| elements.timerState.textContent = 'Cronómetro'; |
| state.timeLeft = 0; |
| updateTimerDisplay(); |
| |
| |
| if (state.timerRunning) { |
| clearInterval(state.timerInterval); |
| state.timerRunning = false; |
| elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar'; |
| } |
| } |
| |
| saveState(); |
| } |
| |
| function toggleTimer() { |
| if (state.timerMode === 'pomodoro') { |
| if (!state.timerRunning) { |
| |
| state.timerRunning = true; |
| elements.startTimer.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar'; |
| |
| state.timerInterval = setInterval(() => { |
| state.timeLeft--; |
| updateTimerDisplay(); |
| |
| if (state.timeLeft <= 0) { |
| |
| clearInterval(state.timerInterval); |
| state.timerRunning = false; |
| elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar'; |
| |
| |
| if (state.soundEnabled) { |
| elements.timerSound.currentTime = 0; |
| elements.timerSound.volume = state.volume; |
| elements.timerSound.play(); |
| } |
| |
| |
| if (elements.timerState.textContent === 'Sesión de Trabajo') { |
| state.focusSessions++; |
| state.timeLeft = state.breakDuration; |
| elements.timerState.textContent = 'Descanso'; |
| addBrainCoins(5); |
| addXP(10); |
| } else { |
| state.timeLeft = state.workDuration; |
| elements.timerState.textContent = 'Sesión de Trabajo'; |
| } |
| |
| updateTimerDisplay(); |
| updateStats(); |
| saveState(); |
| } |
| }, 1000); |
| } else { |
| |
| clearInterval(state.timerInterval); |
| state.timerRunning = false; |
| elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar'; |
| } |
| } else { |
| |
| if (!state.stopwatchRunning) { |
| |
| state.stopwatchRunning = true; |
| elements.startTimer.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar'; |
| |
| state.stopwatchInterval = setInterval(() => { |
| state.stopwatchTime++; |
| updateTimerDisplay(); |
| }, 1000); |
| } else { |
| |
| clearInterval(state.stopwatchInterval); |
| state.stopwatchRunning = false; |
| elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar'; |
| } |
| } |
| |
| saveState(); |
| } |
| |
| function resetTimer() { |
| if (state.timerMode === 'pomodoro') { |
| clearInterval(state.timerInterval); |
| state.timerRunning = false; |
| elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar'; |
| |
| if (elements.timerState.textContent === 'Sesión de Trabajo') { |
| state.timeLeft = state.workDuration; |
| } else { |
| state.timeLeft = state.breakDuration; |
| } |
| } else { |
| clearInterval(state.stopwatchInterval); |
| state.stopwatchRunning = false; |
| state.stopwatchTime = 0; |
| elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar'; |
| } |
| |
| updateTimerDisplay(); |
| saveState(); |
| } |
| |
| function updateTimerDisplay() { |
| if (state.timerMode === 'pomodoro') { |
| const minutes = Math.floor(state.timeLeft / 60); |
| const seconds = state.timeLeft % 60; |
| elements.timeDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; |
| |
| |
| const totalTime = elements.timerState.textContent === 'Sesión de Trabajo' ? state.workDuration : state.breakDuration; |
| const progress = (totalTime - state.timeLeft) / totalTime; |
| const circumference = 283; |
| const offset = circumference - (progress * circumference); |
| elements.progressCircle.style.strokeDashoffset = offset; |
| } else { |
| const hours = Math.floor(state.stopwatchTime / 3600); |
| const minutes = Math.floor((state.stopwatchTime % 3600) / 60); |
| const seconds = state.stopwatchTime % 60; |
| |
| if (hours > 0) { |
| elements.timeDisplay.textContent = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; |
| } else { |
| elements.timeDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; |
| } |
| |
| |
| const progress = (state.stopwatchTime % 60) / 60; |
| const circumference = 283; |
| const offset = circumference - (progress * circumference); |
| elements.progressCircle.style.strokeDashoffset = offset; |
| } |
| } |
| |
| |
| function addTask() { |
| const taskText = elements.newTaskInput.value.trim(); |
| if (taskText) { |
| const newTask = { |
| id: Date.now(), |
| text: taskText, |
| completed: false |
| }; |
| |
| state.tasks.push(newTask); |
| elements.newTaskInput.value = ''; |
| renderTasks(); |
| saveState(); |
| } |
| } |
| |
| function renderTasks() { |
| elements.taskList.innerHTML = ''; |
| state.completedTasks = state.tasks.filter(task => task.completed).length; |
| |
| state.tasks.forEach(task => { |
| const taskElement = document.createElement('div'); |
| taskElement.className = `flex items-center p-3 rounded-lg glass-panel ${task.completed ? 'opacity-70' : ''}`; |
| taskElement.dataset.id = task.id; |
| |
| taskElement.innerHTML = ` |
| <button class="complete-btn w-6 h-6 rounded-full ${task.completed ? 'bg-primary' : 'border border-primary'} flex items-center justify-center mr-3"> |
| ${task.completed ? '<i class="fas fa-check text-dark"></i>' : ''} |
| </button> |
| <span class="flex-grow ${task.completed ? 'line-through' : ''}">${task.text}</span> |
| <button class="delete-btn ml-2 text-gray-400 hover:text-white"> |
| <i class="fas fa-times"></i> |
| </button> |
| `; |
| |
| elements.taskList.appendChild(taskElement); |
| |
| |
| const completeBtn = taskElement.querySelector('.complete-btn'); |
| const deleteBtn = taskElement.querySelector('.delete-btn'); |
| |
| completeBtn.addEventListener('click', () => toggleTaskComplete(task.id)); |
| deleteBtn.addEventListener('click', () => deleteTask(task.id)); |
| }); |
| |
| elements.tasksCompleted.textContent = state.completedTasks; |
| elements.tasksTotal.textContent = state.tasks.length; |
| } |
| |
| function toggleTaskComplete(taskId) { |
| const taskIndex = state.tasks.findIndex(task => task.id === taskId); |
| if (taskIndex !== -1) { |
| const task = state.tasks[taskIndex]; |
| task.completed = !task.completed; |
| |
| if (task.completed) { |
| state.completedTasks++; |
| |
| |
| addBrainCoins(2); |
| addXP(5); |
| |
| |
| if (state.soundEnabled) { |
| elements.completeSound.currentTime = 0; |
| elements.completeSound.volume = state.volume; |
| elements.completeSound.play(); |
| } |
| |
| |
| const taskElement = document.querySelector(`[data-id="${taskId}"]`); |
| taskElement.classList.add('task-complete'); |
| |
| |
| const coin = document.createElement('div'); |
| coin.className = 'coin-reward absolute text-yellow-400 font-bold'; |
| coin.innerHTML = '<i class="fas fa-coins"></i> +2'; |
| taskElement.appendChild(coin); |
| |
| setTimeout(() => { |
| coin.remove(); |
| }, 1000); |
| } else { |
| state.completedTasks--; |
| } |
| |
| renderTasks(); |
| updateStats(); |
| saveState(); |
| } |
| } |
| |
| function deleteTask(taskId) { |
| const taskIndex = state.tasks.findIndex(task => task.id === taskId); |
| if (taskIndex !== -1) { |
| const task = state.tasks[taskIndex]; |
| if (task.completed) { |
| state.completedTasks--; |
| } |
| state.tasks.splice(taskIndex, 1); |
| renderTasks(); |
| updateStats(); |
| saveState(); |
| } |
| } |
| |
| function loadTasks() { |
| const savedTasks = localStorage.getItem('neuroFlowTasks'); |
| if (savedTasks) { |
| state.tasks = JSON.parse(savedTasks); |
| state.completedTasks = state.tasks.filter(task => task.completed).length; |
| renderTasks(); |
| } |
| } |
| |
| |
| function displayQuote() { |
| const quote = quotes[state.currentQuoteIndex]; |
| elements.quoteDisplay.innerHTML = ` |
| <p class="italic text-lg mb-2">"${quote.text}"</p> |
| <p class="text-right text-sm opacity-70">- ${quote.author}</p> |
| `; |
| elements.quoteDisplay.classList.add('quote-transition'); |
| |
| setTimeout(() => { |
| elements.quoteDisplay.classList.remove('quote-transition'); |
| }, 1000); |
| } |
| |
| function showNextQuote() { |
| state.currentQuoteIndex = (state.currentQuoteIndex + 1) % quotes.length; |
| displayQuote(); |
| saveState(); |
| } |
| |
| function showPreviousQuote() { |
| state.currentQuoteIndex = (state.currentQuoteIndex - 1 + quotes.length) % quotes.length; |
| displayQuote(); |
| saveState(); |
| } |
| |
| |
| function addBrainCoins(amount) { |
| state.brainCoins += amount; |
| updateBrainCoins(); |
| |
| |
| const coinsElement = document.getElementById('brain-coins'); |
| coinsElement.classList.add('animate-float'); |
| setTimeout(() => { |
| coinsElement.classList.remove('animate-float'); |
| }, 1000); |
| } |
| |
| function addXP(amount) { |
| state.xpPoints += amount; |
| updateXP(); |
| |
| |
| const xpElement = document.getElementById('xp-points'); |
| xpElement.classList.add('animate-float'); |
| setTimeout(() => { |
| xpElement.classList.remove('animate-float'); |
| }, 1000); |
| } |
| |
| function updateBrainCoins() { |
| elements.brainCoins.textContent = state.brainCoins; |
| } |
| |
| function updateXP() { |
| elements.xpPoints.textContent = state.xpPoints; |
| } |
| |
| |
| function updateStats() { |
| elements.sessionsCount.textContent = state.focusSessions; |
| elements.tasksCompletedCount.textContent = state.completedTasks; |
| |
| |
| const maxSessions = 5; |
| const sessionsPercent = Math.min((state.focusSessions / maxSessions) * 100, 100); |
| elements.sessionsBar.style.width = `${sessionsPercent}%`; |
| |
| const tasksPercent = state.tasks.length > 0 ? (state.completedTasks / state.tasks.length) * 100 : 0; |
| elements.tasksBar.style.width = `${tasksPercent}%`; |
| |
| |
| const productivity = (sessionsPercent * 0.6) + (tasksPercent * 0.4); |
| elements.productivityLevel.textContent = `${Math.round(productivity)}%`; |
| elements.productivityBar.style.width = `${productivity}%`; |
| } |
| |
| |
| function playSound(soundType) { |
| if (!state.soundEnabled) return; |
| |
| |
| stopAllSounds(); |
| |
| state.currentSound = soundType; |
| |
| switch (soundType) { |
| case 'rain': |
| elements.rainSound.volume = state.volume; |
| elements.rainSound.play(); |
| break; |
| case 'forest': |
| elements.forestSound.volume = state.volume; |
| elements.forestSound.play(); |
| break; |
| case 'coffee': |
| elements.coffeeSound.volume = state.volume; |
| elements.coffeeSound.play(); |
| break; |
| case 'white': |
| elements.whiteSound.volume = state.volume; |
| elements.whiteSound.play(); |
| break; |
| } |
| |
| |
| elements.soundBtns.forEach(btn => { |
| if (btn.dataset.sound === soundType) { |
| btn.classList.add('bg-primary', 'text-dark'); |
| btn.classList.remove('glass-panel', 'text-white'); |
| } else { |
| btn.classList.remove('bg-primary', 'text-dark'); |
| btn.classList.add('glass-panel', 'text-white'); |
| } |
| }); |
| |
| saveState(); |
| } |
| |
| function stopAllSounds() { |
| elements.rainSound.pause(); |
| elements.forestSound.pause(); |
| elements.coffeeSound.pause(); |
| elements.whiteSound.pause(); |
| } |
| |
| function toggleSound() { |
| state.soundEnabled = !state.soundEnabled; |
| |
| if (state.soundEnabled) { |
| elements.toggleSound.innerHTML = '<i class="fas fa-volume-up"></i>'; |
| elements.toggleSound.classList.add('bg-primary', 'text-dark'); |
| elements.toggleSound.classList.remove('glass-panel', 'text-white'); |
| |
| if (state.currentSound) { |
| playSound(state.currentSound); |
| } |
| } else { |
| elements.toggleSound.innerHTML = '<i class="fas fa-volume-mute"></i>'; |
| elements.toggleSound.classList.remove('bg-primary', 'text-dark'); |
| elements.toggleSound.classList.add('glass-panel', 'text-white'); |
| stopAllSounds(); |
| } |
| |
| saveState(); |
| } |
| |
| function updateVolume() { |
| state.volume = parseFloat(elements.volumeControl.value); |
| |
| if (state.soundEnabled && state.currentSound) { |
| switch (state.currentSound) { |
| case 'rain': |
| elements.rainSound.volume = state.volume; |
| break; |
| case 'forest': |
| elements.forestSound.volume = state.volume; |
| break; |
| case 'coffee': |
| elements.coffeeSound.volume = state.volume; |
| break; |
| case 'white': |
| elements.whiteSound.volume = state.volume; |
| break; |
| } |
| } |
| |
| saveState(); |
| } |
| |
| |
| function saveState() { |
| localStorage.setItem('neuroFlowState', JSON.stringify({ |
| timerMode: state.timerMode, |
| timeLeft: state.timeLeft, |
| workDuration: state.workDuration, |
| breakDuration: state.breakDuration, |
| currentQuoteIndex: state.currentQuoteIndex, |
| brainCoins: state.brainCoins, |
| xpPoints: state.xpPoints, |
| focusSessions: state.focusSessions, |
| soundEnabled: state.soundEnabled, |
| currentSound: state.currentSound, |
| volume: state.volume, |
| stopwatchTime: state.stopwatchTime |
| })); |
| |
| localStorage.setItem('neuroFlowTasks', JSON.stringify(state.tasks)); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', init); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Yoleo/neuroflow" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |