Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Serene Pomodoro Timer</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); | |
| body { | |
| font-family: 'Poppins', sans-serif; | |
| background-image: url('https://images.unsplash.com/photo-1500382018106-4073a5091250?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'); | |
| background-size: cover; | |
| background-position: center; | |
| background-attachment: fixed; | |
| transition: background-color 0.5s ease; | |
| } | |
| .timer-container { | |
| backdrop-filter: blur(10px); | |
| background-color: rgba(255, 255, 255, 0.85); | |
| box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15); | |
| } | |
| .progress-ring__circle { | |
| transition: stroke-dashoffset 0.35s; | |
| transform: rotate(-90deg); | |
| transform-origin: 50% 50%; | |
| } | |
| .tab-active { | |
| position: relative; | |
| } | |
| .tab-active::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: -8px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 30px; | |
| height: 3px; | |
| background-color: #4F46E5; | |
| border-radius: 3px; | |
| } | |
| .settings-panel { | |
| max-height: 0; | |
| overflow: hidden; | |
| transition: max-height 0.3s ease-out; | |
| } | |
| .settings-open { | |
| max-height: 600px; /* Increased to accommodate sound options */ | |
| overflow-y: auto; /* Add scroll if needed */ | |
| } | |
| /* Custom range slider */ | |
| input[type="range"] { | |
| -webkit-appearance: none; | |
| height: 6px; | |
| background: #E5E7EB; | |
| border-radius: 5px; | |
| background-image: linear-gradient(#4F46E5, #4F46E5); | |
| background-size: 0% 100%; | |
| background-repeat: no-repeat; | |
| } | |
| input[type="range"]::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| height: 20px; | |
| width: 20px; | |
| border-radius: 50%; | |
| background: #4F46E5; | |
| cursor: pointer; | |
| box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1); | |
| transition: all 0.3s ease; | |
| } | |
| input[type="range"]::-webkit-slider-thumb:hover { | |
| transform: scale(1.2); | |
| box-shadow: 0 0 5px rgba(79, 70, 229, 0.5); | |
| } | |
| /* Toggle switch */ | |
| .toggle-checkbox:checked { | |
| right: 0; | |
| border-color: #4F46E5; | |
| } | |
| .toggle-checkbox:checked + .toggle-label { | |
| background-color: #4F46E5; | |
| } | |
| /* Nature sounds selector */ | |
| .sound-option { | |
| transition: all 0.2s ease; | |
| } | |
| .sound-option:hover { | |
| transform: translateY(-2px); | |
| } | |
| .sound-option.selected { | |
| border-color: #4F46E5; | |
| background-color: rgba(79, 70, 229, 0.1); | |
| } | |
| /* Animation for timer buttons */ | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| 100% { transform: scale(1); } | |
| } | |
| .pulse { | |
| animation: pulse 2s infinite; | |
| } | |
| /* Custom scrollbar for settings panel */ | |
| .settings-panel::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .settings-panel::-webkit-scrollbar-track { | |
| background: rgba(0, 0, 0, 0.05); | |
| border-radius: 3px; | |
| } | |
| .settings-panel::-webkit-scrollbar-thumb { | |
| background: rgba(79, 70, 229, 0.3); | |
| border-radius: 3px; | |
| } | |
| .settings-panel::-webkit-scrollbar-thumb:hover { | |
| background: rgba(79, 70, 229, 0.5); | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen flex items-center justify-center p-4"> | |
| <div class="timer-container rounded-2xl p-8 w-full max-w-md"> | |
| <!-- Timer Mode Tabs --> | |
| <div class="flex justify-center mb-8"> | |
| <div class="flex space-x-6 bg-gray-100 p-1 rounded-full"> | |
| <button id="pomodoro-tab" class="tab-active px-4 py-2 text-indigo-600 font-medium focus:outline-none"> | |
| Pomodoro | |
| </button> | |
| <button id="short-break-tab" class="px-4 py-2 text-gray-600 font-medium focus:outline-none"> | |
| Short Break | |
| </button> | |
| <button id="long-break-tab" class="px-4 py-2 text-gray-600 font-medium focus:outline-none"> | |
| Long Break | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Timer Display --> | |
| <div class="flex justify-center mb-8"> | |
| <div class="relative w-64 h-64"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <circle class="text-gray-200" stroke-width="6" stroke="currentColor" fill="transparent" r="45" cx="50" cy="50" /> | |
| <circle id="progress-ring" class="progress-ring__circle text-indigo-500" stroke-width="6" stroke-linecap="round" stroke="currentColor" fill="transparent" r="45" cx="50" cy="50" /> | |
| </svg> | |
| <div class="absolute inset-0 flex flex-col items-center justify-center"> | |
| <div id="time-display" class="text-5xl font-bold text-gray-800">25:00</div> | |
| <button id="timer-control" class="mt-4 px-8 py-2 bg-indigo-600 text-white rounded-full font-medium focus:outline-none hover:bg-indigo-700 transition"> | |
| START | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Current Session Info --> | |
| <div id="session-info" class="text-center text-gray-600 mb-8"> | |
| <p>Session #1 • Focus Time</p> | |
| </div> | |
| <!-- Settings Button --> | |
| <div class="flex justify-center mb-4"> | |
| <button id="settings-button" class="text-gray-600 hover:text-indigo-600 focus:outline-none transition"> | |
| <i class="fas fa-cog text-2xl"></i> | |
| </button> | |
| </div> | |
| <!-- Settings Panel --> | |
| <div id="settings-panel" class="settings-panel bg-gray-50 rounded-xl p-6"> | |
| <h3 class="text-lg font-semibold text-gray-800 mb-4">Timer Settings</h3> | |
| <div class="space-y-6"> | |
| <!-- Pomodoro Duration --> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Pomodoro (minutes)</label> | |
| <div class="flex items-center space-x-4"> | |
| <input id="pomodoro-duration" type="range" min="5" max="60" value="25" class="w-full"> | |
| <span id="pomodoro-value" class="text-gray-700 font-medium w-10 text-center">25</span> | |
| </div> | |
| </div> | |
| <!-- Short Break Duration --> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Short Break (minutes)</label> | |
| <div class="flex items-center space-x-4"> | |
| <input id="short-break-duration" type="range" min="1" max="15" value="5" class="w-full"> | |
| <span id="short-break-value" class="text-gray-700 font-medium w-10 text-center">5</span> | |
| </div> | |
| </div> | |
| <!-- Long Break Duration --> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Long Break (minutes)</label> | |
| <div class="flex items-center space-x-4"> | |
| <input id="long-break-duration" type="range" min="10" max="30" value="15" class="w-full"> | |
| <span id="long-break-value" class="text-gray-700 font-medium w-10 text-center">15</span> | |
| </div> | |
| </div> | |
| <!-- Auto-start Breaks --> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-sm font-medium text-gray-700">Auto-start Breaks</label> | |
| <div class="relative inline-block w-10 mr-2 align-middle select-none"> | |
| <input type="checkbox" id="auto-start-breaks" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"/> | |
| <label for="auto-start-breaks" class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"></label> | |
| </div> | |
| </div> | |
| <!-- Auto-start Pomodoros --> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-sm font-medium text-gray-700">Auto-start Pomodoros</label> | |
| <div class="relative inline-block w-10 mr-2 align-middle select-none"> | |
| <input type="checkbox" id="auto-start-pomodoros" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"/> | |
| <label for="auto-start-pomodoros" class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"></label> | |
| </div> | |
| </div> | |
| <!-- Long Break Interval --> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Long Break Interval (pomodoros)</label> | |
| <div class="flex items-center space-x-4"> | |
| <input id="long-break-interval" type="range" min="2" max="8" value="4" class="w-full"> | |
| <span id="long-break-interval-value" class="text-gray-700 font-medium w-10 text-center">4</span> | |
| </div> | |
| </div> | |
| <!-- Nature Sounds --> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Nature Sounds</label> | |
| <div class="grid grid-cols-3 gap-3"> | |
| <div class="sound-option border rounded-lg p-3 text-center cursor-pointer"> | |
| <i class="fas fa-wind text-xl text-gray-600 mb-1"></i> | |
| <p class="text-xs">Forest</p> | |
| </div> | |
| <div class="sound-option border rounded-lg p-3 text-center cursor-pointer selected"> | |
| <i class="fas fa-water text-xl text-gray-600 mb-1"></i> | |
| <p class="text-xs">Rain</p> | |
| </div> | |
| <div class="sound-option border rounded-lg p-3 text-center cursor-pointer"> | |
| <i class="fas fa-feather text-xl text-gray-600 mb-1"></i> | |
| <p class="text-xs">Birds</p> | |
| </div> | |
| <div class="sound-option border rounded-lg p-3 text-center cursor-pointer"> | |
| <i class="fas fa-fire text-xl text-gray-600 mb-1"></i> | |
| <p class="text-xs">Fire</p> | |
| </div> | |
| <div class="sound-option border rounded-lg p-3 text-center cursor-pointer"> | |
| <i class="fas fa-umbrella-beach text-xl text-gray-600 mb-1"></i> | |
| <p class="text-xs">Waves</p> | |
| </div> | |
| <div class="sound-option border rounded-lg p-3 text-center cursor-pointer"> | |
| <i class="fas fa-volume-mute text-xl text-gray-600 mb-1"></i> | |
| <p class="text-xs">None</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6 flex justify-end"> | |
| <button id="save-settings" class="px-4 py-2 bg-indigo-600 text-white rounded-lg font-medium focus:outline-none hover:bg-indigo-700 transition"> | |
| Save Settings | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <audio id="tick-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-clock-countdown-bleeps-916.mp3" preload="auto"></audio> | |
| <audio id="alarm-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-alarm-digital-clock-beep-989.mp3" preload="auto"></audio> | |
| <audio id="nature-sound" loop src="https://assets.mixkit.co/sfx/preview/mixkit-rain-loop-1243.mp3" preload="auto"></audio> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // DOM Elements | |
| const timeDisplay = document.getElementById('time-display'); | |
| const timerControl = document.getElementById('timer-control'); | |
| const sessionInfo = document.getElementById('session-info'); | |
| const progressRing = document.getElementById('progress-ring'); | |
| const settingsButton = document.getElementById('settings-button'); | |
| const settingsPanel = document.getElementById('settings-panel'); | |
| const saveSettings = document.getElementById('save-settings'); | |
| const pomodoroTab = document.getElementById('pomodoro-tab'); | |
| const shortBreakTab = document.getElementById('short-break-tab'); | |
| const longBreakTab = document.getElementById('long-break-tab'); | |
| const tickSound = document.getElementById('tick-sound'); | |
| const alarmSound = document.getElementById('alarm-sound'); | |
| const natureSound = document.getElementById('nature-sound'); | |
| // Timer state | |
| let timer; | |
| let isRunning = false; | |
| let timeLeft = 25 * 60; // 25 minutes in seconds | |
| let totalTime = 25 * 60; | |
| let currentMode = 'pomodoro'; // 'pomodoro', 'shortBreak', 'longBreak' | |
| let sessionsCompleted = 0; | |
| let longBreakInterval = 4; | |
| let autoStartBreaks = false; | |
| let autoStartPomodoros = false; | |
| let natureSoundEnabled = true; | |
| // Initialize UI | |
| updateTimeDisplay(); | |
| updateProgressRing(); | |
| setupRangeInputs(); | |
| setupSoundOptions(); | |
| // Event Listeners | |
| timerControl.addEventListener('click', toggleTimer); | |
| settingsButton.addEventListener('click', toggleSettings); | |
| saveSettings.addEventListener('click', saveTimerSettings); | |
| pomodoroTab.addEventListener('click', () => switchMode('pomodoro')); | |
| shortBreakTab.addEventListener('click', () => switchMode('shortBreak')); | |
| longBreakTab.addEventListener('click', () => switchMode('longBreak')); | |
| // Timer Functions | |
| function toggleTimer() { | |
| if (isRunning) { | |
| pauseTimer(); | |
| } else { | |
| startTimer(); | |
| } | |
| } | |
| function startTimer() { | |
| if (!isRunning) { | |
| isRunning = true; | |
| timerControl.textContent = 'PAUSE'; | |
| timerControl.classList.remove('bg-indigo-600', 'hover:bg-indigo-700'); | |
| timerControl.classList.add('bg-amber-500', 'hover:bg-amber-600'); | |
| if (natureSoundEnabled) { | |
| natureSound.play().catch(e => console.log("Audio play failed:", e)); | |
| } | |
| timer = setInterval(() => { | |
| timeLeft--; | |
| updateTimeDisplay(); | |
| updateProgressRing(); | |
| // Play tick sound every second | |
| if (timeLeft > 0 && timeLeft % 60 === 0) { | |
| tickSound.currentTime = 0; | |
| tickSound.play().catch(e => console.log("Tick sound play failed:", e)); | |
| } | |
| if (timeLeft <= 0) { | |
| clearInterval(timer); | |
| timerComplete(); | |
| } | |
| }, 1000); | |
| } | |
| } | |
| function pauseTimer() { | |
| if (isRunning) { | |
| isRunning = false; | |
| timerControl.textContent = 'RESUME'; | |
| timerControl.classList.remove('bg-amber-500', 'hover:bg-amber-600'); | |
| timerControl.classList.add('bg-indigo-600', 'hover:bg-indigo-700'); | |
| natureSound.pause(); | |
| clearInterval(timer); | |
| } | |
| } | |
| function timerComplete() { | |
| isRunning = false; | |
| alarmSound.play().catch(e => console.log("Alarm sound play failed:", e)); | |
| natureSound.pause(); | |
| if (currentMode === 'pomodoro') { | |
| sessionsCompleted++; | |
| // Check if it's time for a long break | |
| if (sessionsCompleted % longBreakInterval === 0) { | |
| if (autoStartBreaks) { | |
| setTimeout(() => switchMode('longBreak'), 1000); | |
| } else { | |
| timerControl.textContent = 'START LONG BREAK'; | |
| updateSessionInfo(`Pomodoro completed! Take a long break.`); | |
| } | |
| } else { | |
| if (autoStartBreaks) { | |
| setTimeout(() => switchMode('shortBreak'), 1000); | |
| } else { | |
| timerControl.textContent = 'START SHORT BREAK'; | |
| updateSessionInfo(`Pomodoro completed! Take a short break.`); | |
| } | |
| } | |
| } else { | |
| if (autoStartPomodoros) { | |
| setTimeout(() => switchMode('pomodoro'), 1000); | |
| } else { | |
| timerControl.textContent = 'START POMODORO'; | |
| updateSessionInfo(`Break completed! Ready for next pomodoro?`); | |
| } | |
| } | |
| timerControl.classList.remove('bg-amber-500', 'hover:bg-amber-600'); | |
| timerControl.classList.add('bg-green-500', 'hover:bg-green-600', 'pulse'); | |
| } | |
| function switchMode(mode) { | |
| // Reset timer state | |
| clearInterval(timer); | |
| isRunning = false; | |
| timerControl.classList.remove('pulse', 'bg-green-500', 'hover:bg-green-600', 'bg-amber-500', 'hover:bg-amber-600'); | |
| timerControl.classList.add('bg-indigo-600', 'hover:bg-indigo-700'); | |
| timerControl.textContent = 'START'; | |
| natureSound.pause(); | |
| // Update UI for selected tab | |
| pomodoroTab.classList.remove('tab-active', 'text-indigo-600'); | |
| pomodoroTab.classList.add('text-gray-600'); | |
| shortBreakTab.classList.remove('tab-active', 'text-indigo-600'); | |
| shortBreakTab.classList.add('text-gray-600'); | |
| longBreakTab.classList.remove('tab-active', 'text-indigo-600'); | |
| longBreakTab.classList.add('text-gray-600'); | |
| // Set new mode | |
| currentMode = mode; | |
| // Set time based on mode | |
| if (mode === 'pomodoro') { | |
| timeLeft = parseInt(document.getElementById('pomodoro-duration').value) * 60; | |
| totalTime = timeLeft; | |
| pomodoroTab.classList.add('tab-active', 'text-indigo-600'); | |
| updateSessionInfo(`Session #${Math.floor(sessionsCompleted/longBreakInterval) + 1} • Focus Time`); | |
| } else if (mode === 'shortBreak') { | |
| timeLeft = parseInt(document.getElementById('short-break-duration').value) * 60; | |
| totalTime = timeLeft; | |
| shortBreakTab.classList.add('tab-active', 'text-indigo-600'); | |
| updateSessionInfo(`Short Break • Relax`); | |
| } else if (mode === 'longBreak') { | |
| timeLeft = parseInt(document.getElementById('long-break-duration').value) * 60; | |
| totalTime = timeLeft; | |
| longBreakTab.classList.add('tab-active', 'text-indigo-600'); | |
| updateSessionInfo(`Long Break • Recharge`); | |
| } | |
| updateTimeDisplay(); | |
| updateProgressRing(); | |
| // Auto-start if enabled | |
| if ((mode === 'shortBreak' || mode === 'longBreak') && autoStartBreaks) { | |
| startTimer(); | |
| } else if (mode === 'pomodoro' && autoStartPomodoros) { | |
| startTimer(); | |
| } | |
| } | |
| function updateTimeDisplay() { | |
| const minutes = Math.floor(timeLeft / 60); | |
| const seconds = timeLeft % 60; | |
| timeDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; | |
| } | |
| function updateProgressRing() { | |
| const circumference = 2 * Math.PI * 45; | |
| const offset = circumference - (timeLeft / totalTime) * circumference; | |
| progressRing.style.strokeDasharray = `${circumference} ${circumference}`; | |
| progressRing.style.strokeDashoffset = offset; | |
| // Change color based on time left | |
| if (timeLeft / totalTime < 0.25) { | |
| progressRing.classList.remove('text-indigo-500', 'text-amber-500'); | |
| progressRing.classList.add('text-red-500'); | |
| } else if (timeLeft / totalTime < 0.5) { | |
| progressRing.classList.remove('text-indigo-500', 'text-red-500'); | |
| progressRing.classList.add('text-amber-500'); | |
| } else { | |
| progressRing.classList.remove('text-amber-500', 'text-red-500'); | |
| progressRing.classList.add('text-indigo-500'); | |
| } | |
| } | |
| function updateSessionInfo(text) { | |
| sessionInfo.textContent = text; | |
| } | |
| // Settings Functions | |
| function toggleSettings() { | |
| settingsPanel.classList.toggle('settings-open'); | |
| settingsButton.innerHTML = settingsPanel.classList.contains('settings-open') ? | |
| '<i class="fas fa-times text-2xl"></i>' : '<i class="fas fa-cog text-2xl"></i>'; | |
| } | |
| function setupRangeInputs() { | |
| // Pomodoro duration | |
| const pomodoroDuration = document.getElementById('pomodoro-duration'); | |
| const pomodoroValue = document.getElementById('pomodoro-value'); | |
| pomodoroDuration.addEventListener('input', () => { | |
| pomodoroValue.textContent = pomodoroDuration.value; | |
| updateBackgroundSize(pomodoroDuration); | |
| }); | |
| updateBackgroundSize(pomodoroDuration); | |
| // Short break duration | |
| const shortBreakDuration = document.getElementById('short-break-duration'); | |
| const shortBreakValue = document.getElementById('short-break-value'); | |
| shortBreakDuration.addEventListener('input', () => { | |
| shortBreakValue.textContent = shortBreakDuration.value; | |
| updateBackgroundSize(shortBreakDuration); | |
| }); | |
| updateBackgroundSize(shortBreakDuration); | |
| // Long break duration | |
| const longBreakDuration = document.getElementById('long-break-duration'); | |
| const longBreakValue = document.getElementById('long-break-value'); | |
| longBreakDuration.addEventListener('input', () => { | |
| longBreakValue.textContent = longBreakDuration.value; | |
| updateBackgroundSize(longBreakDuration); | |
| }); | |
| updateBackgroundSize(longBreakDuration); | |
| // Long break interval | |
| const longBreakIntervalInput = document.getElementById('long-break-interval'); | |
| const longBreakIntervalValue = document.getElementById('long-break-interval-value'); | |
| longBreakIntervalInput.addEventListener('input', () => { | |
| longBreakIntervalValue.textContent = longBreakIntervalInput.value; | |
| updateBackgroundSize(longBreakIntervalInput); | |
| }); | |
| updateBackgroundSize(longBreakIntervalInput); | |
| } | |
| function updateBackgroundSize(rangeInput) { | |
| const value = rangeInput.value; | |
| const min = rangeInput.min ? rangeInput.min : 0; | |
| const max = rangeInput.max ? rangeInput.max : 100; | |
| const percentage = (value - min) / (max - min) * 100; | |
| rangeInput.style.backgroundSize = `${percentage}% 100%`; | |
| } | |
| function setupSoundOptions() { | |
| const soundOptions = document.querySelectorAll('.sound-option'); | |
| soundOptions.forEach(option => { | |
| option.addEventListener('click', function() { | |
| soundOptions.forEach(opt => opt.classList.remove('selected')); | |
| this.classList.add('selected'); | |
| // Change nature sound based on selection | |
| const soundType = this.querySelector('p').textContent.toLowerCase(); | |
| changeNatureSound(soundType); | |
| }); | |
| }); | |
| } | |
| function changeNatureSound(soundType) { | |
| if (soundType === 'none') { | |
| natureSoundEnabled = false; | |
| natureSound.pause(); | |
| return; | |
| } | |
| natureSoundEnabled = true; | |
| let soundSrc = ''; | |
| switch(soundType) { | |
| case 'forest': | |
| soundSrc = 'https://assets.mixkit.co/sfx/preview/mixkit-forest-stream-1353.mp3'; | |
| break; | |
| case 'rain': | |
| soundSrc = 'https://assets.mixkit.co/sfx/preview/mixkit-rain-loop-1243.mp3'; | |
| break; | |
| case 'birds': | |
| soundSrc = 'https://assets.mixkit.co/sfx/preview/mixkit-birds-chirping-1480.mp3'; | |
| break; | |
| case 'fire': | |
| soundSrc = 'https://assets.mixkit.co/sfx/preview/mixkit-crackling-fireplace-1352.mp3'; | |
| break; | |
| case 'waves': | |
| soundSrc = 'https://assets.mixkit.co/sfx/preview/mixkit-waves-coming-to-shore-1170.mp3'; | |
| break; | |
| default: | |
| soundSrc = 'https://assets.mixkit.co/sfx/preview/mixkit-rain-loop-1243.mp3'; | |
| } | |
| natureSound.src = soundSrc; | |
| if (isRunning) { | |
| natureSound.play().catch(e => console.log("Nature sound play failed:", e)); | |
| } | |
| } | |
| function saveTimerSettings() { | |
| // Get values from inputs | |
| const pomodoroMinutes = parseInt(document.getElementById('pomodoro-duration').value); | |
| const shortBreakMinutes = parseInt(document.getElementById('short-break-duration').value); | |
| const longBreakMinutes = parseInt(document.getElementById('long-break-duration').value); | |
| longBreakInterval = parseInt(document.getElementById('long-break-interval').value); | |
| autoStartBreaks = document.getElementById('auto-start-breaks').checked; | |
| autoStartPomodoros = document.getElementById('auto-start-pomodoros').checked; | |
| // Update current timer if needed | |
| if (!isRunning) { | |
| if (currentMode === 'pomodoro') { | |
| timeLeft = pomodoroMinutes * 60; | |
| totalTime = timeLeft; | |
| } else if (currentMode === 'shortBreak') { | |
| timeLeft = shortBreakMinutes * 60; | |
| totalTime = timeLeft; | |
| } else if (currentMode === 'longBreak') { | |
| timeLeft = longBreakMinutes * 60; | |
| totalTime = timeLeft; | |
| } | |
| updateTimeDisplay(); | |
| updateProgressRing(); | |
| } | |
| // Close settings | |
| toggleSettings(); | |
| // Show confirmation | |
| const originalText = saveSettings.textContent; | |
| saveSettings.textContent = 'Saved!'; | |
| saveSettings.classList.remove('bg-indigo-600'); | |
| saveSettings.classList.add('bg-green-500'); | |
| setTimeout(() => { | |
| saveSettings.textContent = originalText; | |
| saveSettings.classList.remove('bg-green-500'); | |
| saveSettings.classList.add('bg-indigo-600'); | |
| }, 2000); | |
| } | |
| }); | |
| </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=adeism/padomoro-timer" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |