neuroflow / index.html
Yoleo's picture
Add 2 files
2755226 verified
<!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">
<!-- Background elements -->
<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>
<!-- Main App Container -->
<div class="container mx-auto px-4 py-8 max-w-6xl relative z-10">
<!-- Header -->
<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>
<!-- Main Content Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Left Column -->
<div class="lg:col-span-2 space-y-6">
<!-- Timer Section -->
<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">
<!-- Circular Timer -->
<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>
<!-- To-Do List Section -->
<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">
<!-- Tasks will be added here dynamically -->
</div>
</div>
</div>
<!-- Right Column -->
<div class="space-y-6">
<!-- Focus Enhancer -->
<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>
<!-- Stats Panel -->
<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 -->
<footer class="mt-12 text-center text-sm opacity-60">
<p>Creado por Reshman</p>
</footer>
</div>
<!-- Audio Elements -->
<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>
// App State
const state = {
timerMode: 'pomodoro',
timerRunning: false,
timeLeft: 25 * 60, // 25 minutes in seconds
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
};
// Motivational Quotes (translated to Spanish)
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" }
];
// DOM Elements
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')
};
// Initialize the app
function init() {
displayQuote();
updateTimerDisplay();
updateStats();
loadTasks();
// Event Listeners
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));
});
// Load saved state from localStorage
const savedState = localStorage.getItem('neuroFlowState');
if (savedState) {
const parsedState = JSON.parse(savedState);
Object.assign(state, parsedState);
// Update UI with saved state
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;
}
}
// Timer Functions
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();
// Stop stopwatch if running
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();
// Stop pomodoro timer if running
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) {
// Start the timer
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) {
// Timer completed
clearInterval(state.timerInterval);
state.timerRunning = false;
elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar';
// Play timer sound
if (state.soundEnabled) {
elements.timerSound.currentTime = 0;
elements.timerSound.volume = state.volume;
elements.timerSound.play();
}
// Switch between work and break
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 {
// Pause the timer
clearInterval(state.timerInterval);
state.timerRunning = false;
elements.startTimer.innerHTML = '<i class="fas fa-play mr-2"></i> Iniciar';
}
} else {
// Stopwatch mode
if (!state.stopwatchRunning) {
// Start the stopwatch
state.stopwatchRunning = true;
elements.startTimer.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar';
state.stopwatchInterval = setInterval(() => {
state.stopwatchTime++;
updateTimerDisplay();
}, 1000);
} else {
// Pause the stopwatch
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')}`;
// Update progress circle
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')}`;
}
// Update progress circle for stopwatch (just spins continuously)
const progress = (state.stopwatchTime % 60) / 60;
const circumference = 283;
const offset = circumference - (progress * circumference);
elements.progressCircle.style.strokeDashoffset = offset;
}
}
// Task List Functions
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);
// Add event listeners
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++;
// Reward for completing a task
addBrainCoins(2);
addXP(5);
// Play completion sound
if (state.soundEnabled) {
elements.completeSound.currentTime = 0;
elements.completeSound.volume = state.volume;
elements.completeSound.play();
}
// Visual feedback
const taskElement = document.querySelector(`[data-id="${taskId}"]`);
taskElement.classList.add('task-complete');
// Create coin reward animation
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();
}
}
// Quote Functions
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();
}
// Reward System Functions
function addBrainCoins(amount) {
state.brainCoins += amount;
updateBrainCoins();
// Visual feedback
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();
// Visual feedback
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;
}
// Stats Functions
function updateStats() {
elements.sessionsCount.textContent = state.focusSessions;
elements.tasksCompletedCount.textContent = state.completedTasks;
// Update progress bars
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}%`;
// Calculate productivity level (weighted average)
const productivity = (sessionsPercent * 0.6) + (tasksPercent * 0.4);
elements.productivityLevel.textContent = `${Math.round(productivity)}%`;
elements.productivityBar.style.width = `${productivity}%`;
}
// Sound Functions
function playSound(soundType) {
if (!state.soundEnabled) return;
// Stop any currently playing sound
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;
}
// Update active button
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();
}
// Save state to localStorage
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));
}
// Initialize the app when DOM is loaded
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>