Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Circular Timer with Progress Bar</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| .glow { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 50%; | |
| background: radial-gradient(circle, rgba(255,255,255,0.4) 0%, rgba(255,255,255,0) 70%); | |
| filter: blur(10px); | |
| z-index: -1; | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| } | |
| .timer-container:hover .glow { | |
| opacity: 1; | |
| } | |
| @keyframes progress { | |
| 0% { stroke-dashoffset: 283; } | |
| 100% { stroke-dashoffset: 0; } | |
| } | |
| .progress-circle { | |
| transform: rotate(-90deg); | |
| transform-origin: 50% 50%; | |
| } | |
| .clock-face { | |
| box-shadow: 0 0 15px rgba(255, 255, 255, 0.3); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-black min-h-screen flex flex-col items-center justify-center p-4"> | |
| <div class="max-w-md w-full mx-auto"> | |
| <h1 class="text-white text-3xl font-bold mb-4 text-center">Focus Flow Timer</h1> | |
| <!-- Tag Management Section --> | |
| <div class="mb-4 w-full max-w-md"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <label class="block text-white">Select Tag</label> | |
| <button id="manage-tags-btn" class="text-blue-400 hover:text-blue-300 text-sm"> | |
| Manage Tags | |
| </button> | |
| </div> | |
| <select id="tags-input" class="w-full bg-gray-800 text-white rounded p-2"> | |
| <!-- Tags will be loaded dynamically --> | |
| </select> | |
| </div> | |
| <!-- Tag Management Modal --> | |
| <div id="tag-modal" class="hidden fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50"> | |
| <div class="bg-gray-800 rounded-lg p-6 w-full max-w-md"> | |
| <h3 class="text-white text-xl font-bold mb-4">Manage Tags</h3> | |
| <div class="mb-4"> | |
| <input id="new-tag-input" type="text" placeholder="New tag name" | |
| class="w-full bg-gray-700 text-white rounded p-2 mb-2"> | |
| <textarea id="tag-description" placeholder="Tag description (optional)" | |
| class="w-full bg-gray-700 text-white rounded p-2 mb-2"></textarea> | |
| <button id="add-tag-btn" class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded w-full"> | |
| Add Tag | |
| </button> | |
| </div> | |
| <div id="tags-list" class="max-h-60 overflow-y-auto mb-4"> | |
| <!-- Tags will be listed here --> | |
| </div> | |
| <div class="flex justify-end space-x-2"> | |
| <button id="close-tag-modal" class="bg-gray-600 hover:bg-gray-700 text-white py-2 px-4 rounded"> | |
| Close | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="relative w-64 h-64 mx-auto mb-8 timer-container"> | |
| <div class="glow"></div> | |
| <button id="fullscreen-btn" class="absolute top-2 right-2 bg-gray-800 bg-opacity-50 text-white p-1 rounded-full z-10"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" /> | |
| </svg> | |
| </button> | |
| <!-- Progress bar circle --> | |
| <svg class="absolute top-0 left-0 w-full h-full" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#2d3748" stroke-width="8"/> | |
| <circle id="progress-bar" class="progress-circle" cx="50" cy="50" r="45" fill="none" stroke="#ffffff" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283"/> | |
| </svg> | |
| <!-- Clock face --> | |
| <div class="clock-face absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-48 h-48 bg-white rounded-full flex items-center justify-center"> | |
| <div id="timer-display" class="text-4xl font-bold">00:00:00</div> | |
| </div> | |
| </div> | |
| <!-- Controls --> | |
| <div class="bg-gray-900 rounded-lg p-6"> | |
| <div class="mb-4"> | |
| <label class="block text-white mb-2">Set Timer Duration</label> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <div> | |
| <label class="text-gray-300 text-sm">Hours</label> | |
| <input id="hours" type="number" min="0" max="23" value="0" class="w-full bg-gray-800 text-white rounded p-2"> | |
| </div> | |
| <div> | |
| <label class="text-gray-300 text-sm">Minutes</label> | |
| <input id="minutes" type="number" min="0" max="59" value="0" class="w-full bg-gray-800 text-white rounded p-2"> | |
| </div> | |
| <div> | |
| <label class="text-gray-300 text-sm">Seconds</label> | |
| <input id="seconds" type="number" min="0" max="59" value="10" class="w-full bg-gray-800 text-white rounded p-2"> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex space-x-4 mb-4"> | |
| <button id="start-btn" class="flex-1 bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded transition"> | |
| Start | |
| </button> | |
| <button id="pause-btn" class="flex-1 bg-yellow-600 hover:bg-yellow-700 text-white py-2 px-4 rounded transition" disabled> | |
| Pause | |
| </button> | |
| <button id="reset-btn" class="flex-1 bg-red-600 hover:bg-red-700 text-white py-2 px-4 rounded transition"> | |
| Reset | |
| </button> | |
| </div> | |
| </div> | |
| <!-- History Section --> | |
| <div class="mt-8 w-full max-w-md"> | |
| <h2 class="text-white text-xl font-bold mb-4">Timer History</h2> | |
| <div id="history-list" class="bg-gray-900 rounded-lg p-4 max-h-64 overflow-y-auto"> | |
| <!-- History items will be added here dynamically --> | |
| </div> | |
| </div> | |
| <!-- Analytics Section --> | |
| <div class="mt-8 w-full max-w-md"> | |
| <h2 class="text-white text-xl font-bold mb-4">Time Analytics</h2> | |
| <div class="bg-gray-900 rounded-lg p-4"> | |
| <div class="mb-4"> | |
| <label class="block text-white mb-2">Filter by Tag</label> | |
| <select id="analytics-tag-filter" class="w-full bg-gray-800 text-white rounded p-2"> | |
| <option value="all">All Tags</option> | |
| <option value="work">Work</option> | |
| <option value="study">Study</option> | |
| <option value="exercise">Exercise</option> | |
| <option value="break">Break</option> | |
| <option value="meeting">Meeting</option> | |
| <option value="creative">Creative</option> | |
| <option value="other">Other</option> | |
| </select> | |
| </div> | |
| <div id="analytics-results" class="text-white"> | |
| <div class="flex justify-between mb-2"> | |
| <span>Total Time Tracked:</span> | |
| <span id="total-time">00:00:00</span> | |
| </div> | |
| <div class="grid grid-cols-2 gap-4 mb-4"> | |
| <div class="bg-gray-800 p-3 rounded"> | |
| <div class="text-gray-400 text-sm">Today</div> | |
| <div id="today-time">00:00:00</div> | |
| </div> | |
| <div class="bg-gray-800 p-3 rounded"> | |
| <div class="text-gray-400 text-sm">This Week</div> | |
| <div id="week-time">00:00:00</div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-800 p-4 rounded"> | |
| <h3 class="text-gray-400 text-sm mb-2">Time Distribution</h3> | |
| <canvas id="time-chart" height="200"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const timerDisplay = document.getElementById('timer-display'); | |
| const progressBar = document.getElementById('progress-bar'); | |
| const hoursInput = document.getElementById('hours'); | |
| const minutesInput = document.getElementById('minutes'); | |
| const secondsInput = document.getElementById('seconds'); | |
| const startBtn = document.getElementById('start-btn'); | |
| const pauseBtn = document.getElementById('pause-btn'); | |
| const resetBtn = document.getElementById('reset-btn'); | |
| let totalSeconds = 0; | |
| let remainingSeconds = 0; | |
| let timerInterval; | |
| let isPaused = false; | |
| let animationDuration = 0; | |
| // Format time to HH:MM:SS | |
| function formatTime(seconds) { | |
| const hrs = Math.floor(seconds / 3600); | |
| const mins = Math.floor((seconds % 3600) / 60); | |
| const secs = seconds % 60; | |
| return `${String(hrs).padStart(2, '0')}:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; | |
| } | |
| // Update the timer display | |
| function updateDisplay() { | |
| timerDisplay.textContent = formatTime(remainingSeconds); | |
| } | |
| // Start the timer | |
| function startTimer() { | |
| const hours = parseInt(hoursInput.value) || 0; | |
| const minutes = parseInt(minutesInput.value) || 0; | |
| const seconds = parseInt(secondsInput.value) || 0; | |
| totalSeconds = hours * 3600 + minutes * 60 + seconds; | |
| remainingSeconds = totalSeconds; | |
| if (totalSeconds <= 0) { | |
| alert('Please set a valid time!'); | |
| return; | |
| } | |
| // Calculate animation duration based on total time | |
| animationDuration = totalSeconds; | |
| // Reset progress bar | |
| progressBar.style.animation = 'none'; | |
| progressBar.offsetHeight; // Trigger reflow | |
| progressBar.style.animation = `progress ${animationDuration}s linear forwards`; | |
| updateDisplay(); | |
| timerInterval = setInterval(() => { | |
| if (!isPaused) { | |
| remainingSeconds--; | |
| updateDisplay(); | |
| if (remainingSeconds <= 0) { | |
| clearInterval(timerInterval); | |
| startBtn.disabled = false; | |
| pauseBtn.disabled = true; | |
| // Reset timer automatically | |
| resetTimer(); | |
| saveToHistory(); | |
| // Play sound after reset completes | |
| const audio = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-alarm-digital-clock-beep-989.mp3'); | |
| audio.play(); | |
| } | |
| } | |
| }, 1000); | |
| startBtn.disabled = true; | |
| pauseBtn.disabled = false; | |
| isPaused = false; | |
| } | |
| // Pause the timer | |
| function pauseTimer() { | |
| isPaused = !isPaused; | |
| if (isPaused) { | |
| pauseBtn.textContent = 'Resume'; | |
| progressBar.style.animationPlayState = 'paused'; | |
| } else { | |
| pauseBtn.textContent = 'Pause'; | |
| progressBar.style.animationPlayState = 'running'; | |
| } | |
| } | |
| // Reset the timer | |
| function resetTimer() { | |
| clearInterval(timerInterval); | |
| progressBar.style.animation = 'none'; | |
| remainingSeconds = 0; | |
| updateDisplay(); | |
| startBtn.disabled = false; | |
| pauseBtn.disabled = true; | |
| pauseBtn.textContent = 'Pause'; | |
| isPaused = false; | |
| } | |
| // Save timer to history | |
| function saveToHistory() { | |
| const tagsInput = document.getElementById('tags-input'); | |
| const tag = tagsInput.value; | |
| const duration = totalSeconds; | |
| const date = new Date().toLocaleString(); | |
| if (totalSeconds > 0) { | |
| const historyItem = document.createElement('div'); | |
| historyItem.className = 'mb-2 pb-2 border-b border-gray-700'; | |
| historyItem.innerHTML = ` | |
| <div class="text-white">${tag}</div> | |
| <div class="flex justify-between text-gray-400 text-sm"> | |
| <span>${formatTime(duration)}</span> | |
| <span>${date}</span> | |
| </div> | |
| `; | |
| document.getElementById('history-list').prepend(historyItem); | |
| // Add to analytics | |
| addToAnalytics(tag, duration); | |
| } | |
| } | |
| // Event listeners | |
| startBtn.addEventListener('click', startTimer); | |
| pauseBtn.addEventListener('click', pauseTimer); | |
| resetBtn.addEventListener('click', () => { | |
| resetTimer(); | |
| if (remainingSeconds <= 0 && totalSeconds > 0) { | |
| saveToHistory(); | |
| } | |
| }); | |
| // Initialize display | |
| updateDisplay(); | |
| // Analytics data storage | |
| let analyticsData = []; | |
| // Load any existing history from localStorage | |
| function loadHistory() { | |
| const savedData = localStorage.getItem('timerAnalytics'); | |
| if (savedData) { | |
| analyticsData = JSON.parse(savedData); | |
| updateAnalytics(); | |
| } | |
| } | |
| // Save analytics data to localStorage | |
| function saveAnalyticsData() { | |
| localStorage.setItem('timerAnalytics', JSON.stringify(analyticsData)); | |
| } | |
| // Update analytics display | |
| function updateAnalytics() { | |
| const filter = document.getElementById('analytics-tag-filter').value; | |
| const filteredData = filter === 'all' ? analyticsData : | |
| analyticsData.filter(item => item.tag === filter); | |
| // Calculate total time | |
| const totalSeconds = filteredData.reduce((sum, item) => sum + item.duration, 0); | |
| document.getElementById('total-time').textContent = formatTime(totalSeconds); | |
| // Calculate today's time | |
| const today = new Date().toLocaleDateString(); | |
| const todaySeconds = filteredData | |
| .filter(item => new Date(item.date).toLocaleDateString() === today) | |
| .reduce((sum, item) => sum + item.duration, 0); | |
| document.getElementById('today-time').textContent = formatTime(todaySeconds); | |
| // Calculate this week's time | |
| const oneWeekAgo = new Date(); | |
| oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); | |
| const weekSeconds = filteredData | |
| .filter(item => new Date(item.date) > oneWeekAgo) | |
| .reduce((sum, item) => sum + item.duration, 0); | |
| document.getElementById('week-time').textContent = formatTime(weekSeconds); | |
| } | |
| // Add data to analytics | |
| function addToAnalytics(tag, duration) { | |
| analyticsData.push({ | |
| tag, | |
| duration, | |
| date: new Date().toISOString() | |
| }); | |
| saveAnalyticsData(); | |
| updateAnalytics(); | |
| } | |
| // Event listener for analytics filter | |
| document.getElementById('analytics-tag-filter').addEventListener('change', updateAnalytics); | |
| loadHistory(); | |
| loadTags(); | |
| // Tag management functionality | |
| const tagModal = document.getElementById('tag-modal'); | |
| const manageTagsBtn = document.getElementById('manage-tags-btn'); | |
| const closeTagModal = document.getElementById('close-tag-modal'); | |
| const addTagBtn = document.getElementById('add-tag-btn'); | |
| const newTagInput = document.getElementById('new-tag-input'); | |
| const tagsList = document.getElementById('tags-list'); | |
| let userTags = [ | |
| {name: 'work', description: 'Work related tasks'}, | |
| {name: 'study', description: 'Learning and studying'}, | |
| {name: 'exercise', description: 'Physical activity'}, | |
| {name: 'break', description: 'Rest periods'}, | |
| {name: 'meeting', description: 'Meetings and calls'}, | |
| {name: 'creative', description: 'Creative work'}, | |
| {name: 'other', description: 'Other activities'} | |
| ]; | |
| // Load tags from localStorage | |
| function loadTags() { | |
| const savedTags = localStorage.getItem('userTags'); | |
| if (savedTags) { | |
| userTags = JSON.parse(savedTags); | |
| } | |
| updateTagDropdown(); | |
| } | |
| // Save tags to localStorage | |
| function saveTags() { | |
| localStorage.setItem('userTags', JSON.stringify(userTags)); | |
| updateTagDropdown(); | |
| updateAnalytics(); | |
| } | |
| // Update tag dropdown | |
| function updateTagDropdown() { | |
| const tagsInput = document.getElementById('tags-input'); | |
| const analyticsFilter = document.getElementById('analytics-tag-filter'); | |
| // Clear existing options | |
| tagsInput.innerHTML = ''; | |
| analyticsFilter.innerHTML = '<option value="all">All Tags</option>'; | |
| // Sort tags alphabetically | |
| const sortedTags = [...userTags].sort((a, b) => a.name.localeCompare(b.name)); | |
| // Add new options | |
| sortedTags.forEach(tag => { | |
| const option = document.createElement('option'); | |
| option.value = tag.name; | |
| option.textContent = tag.name.charAt(0).toUpperCase() + tag.name.slice(1); | |
| option.title = tag.description || ''; | |
| tagsInput.appendChild(option); | |
| const filterOption = document.createElement('option'); | |
| filterOption.value = tag.name; | |
| filterOption.textContent = tag.name.charAt(0).toUpperCase() + tag.name.slice(1); | |
| filterOption.title = tag.description || ''; | |
| analyticsFilter.appendChild(filterOption); | |
| }); | |
| // Select the first tag by default | |
| if (sortedTags.length > 0) { | |
| tagsInput.value = sortedTags[0].name; | |
| } | |
| } | |
| // Update tags list in modal | |
| function updateTagsList() { | |
| tagsList.innerHTML = ''; | |
| userTags.forEach((tag, index) => { | |
| const tagItem = document.createElement('div'); | |
| tagItem.className = 'mb-2 p-2 bg-gray-700 rounded'; | |
| tagItem.innerHTML = ` | |
| <div class="flex items-center justify-between mb-1"> | |
| <span class="text-white font-medium">${tag.name}</span> | |
| <button data-index="${index}" class="delete-tag text-red-400 hover:text-red-300"> | |
| Delete | |
| </button> | |
| </div> | |
| <div class="text-gray-400 text-sm">${tag.description || 'No description'}</div> | |
| `; | |
| tagsList.appendChild(tagItem); | |
| }); | |
| // Add event listeners to delete buttons | |
| document.querySelectorAll('.delete-tag').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| const index = e.target.getAttribute('data-index'); | |
| if (userTags.length > 1) { | |
| userTags.splice(index, 1); | |
| saveTags(); | |
| updateTagsList(); | |
| } else { | |
| alert('You must have at least one tag!'); | |
| } | |
| }); | |
| }); | |
| } | |
| // Event listeners for tag management | |
| manageTagsBtn.addEventListener('click', () => { | |
| tagModal.classList.remove('hidden'); | |
| updateTagsList(); | |
| }); | |
| closeTagModal.addEventListener('click', () => { | |
| tagModal.classList.add('hidden'); | |
| }); | |
| // Handle Enter key press in tag input | |
| newTagInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| addTagBtn.click(); | |
| } | |
| }); | |
| // Handle Enter key press in description | |
| document.getElementById('tag-description').addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| addTagBtn.click(); | |
| } | |
| }); | |
| addTagBtn.addEventListener('click', () => { | |
| const newTag = newTagInput.value.trim().toLowerCase(); | |
| const description = document.getElementById('tag-description').value.trim(); | |
| if (!newTag) { | |
| alert('Please enter a tag name'); | |
| newTagInput.focus(); | |
| return; | |
| } | |
| const tagExists = userTags.some(t => t.name === newTag); | |
| if (tagExists) { | |
| alert('This tag already exists'); | |
| return; | |
| } | |
| userTags.push({ | |
| name: newTag, | |
| description: description | |
| }); | |
| saveTags(); | |
| updateTagsList(); | |
| newTagInput.value = ''; | |
| document.getElementById('tag-description').value = ''; | |
| newTagInput.focus(); | |
| }); | |
| // Initialize time chart | |
| let timeChart = null; | |
| function renderTimeChart(filteredData) { | |
| const ctx = document.getElementById('time-chart').getContext('2d'); | |
| // Group by tag and sum durations | |
| const tagData = {}; | |
| filteredData.forEach(item => { | |
| if (!tagData[item.tag]) { | |
| tagData[item.tag] = 0; | |
| } | |
| tagData[item.tag] += item.duration; | |
| }); | |
| const labels = Object.keys(tagData); | |
| const data = Object.values(tagData); | |
| // Convert seconds to hours for better readability | |
| const hoursData = data.map(seconds => (seconds / 3600).toFixed(2)); | |
| // Generate colors for each tag | |
| const backgroundColors = labels.map((_, i) => { | |
| const hue = (i * 360 / labels.length) % 360; | |
| return `hsl(${hue}, 70%, 60%)`; | |
| }); | |
| if (timeChart) { | |
| timeChart.destroy(); | |
| } | |
| timeChart = new Chart(ctx, { | |
| type: 'pie', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| data: hoursData, | |
| backgroundColor: backgroundColors, | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| color: '#fff' | |
| } | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| const hours = context.raw; | |
| const minutes = (hours % 1) * 60; | |
| return `${context.label}: ${Math.floor(hours)}h ${Math.round(minutes)}m`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Update analytics to include chart | |
| function updateAnalytics() { | |
| const filter = document.getElementById('analytics-tag-filter').value; | |
| const filteredData = filter === 'all' ? analyticsData : | |
| analyticsData.filter(item => item.tag === filter); | |
| // Calculate total time | |
| const totalSeconds = filteredData.reduce((sum, item) => sum + item.duration, 0); | |
| document.getElementById('total-time').textContent = formatTime(totalSeconds); | |
| // Calculate today's time | |
| const today = new Date().toLocaleDateString(); | |
| const todaySeconds = filteredData | |
| .filter(item => new Date(item.date).toLocaleDateString() === today) | |
| .reduce((sum, item) => sum + item.duration, 0); | |
| document.getElementById('today-time').textContent = formatTime(todaySeconds); | |
| // Calculate this week's time | |
| const oneWeekAgo = new Date(); | |
| oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); | |
| const weekSeconds = filteredData | |
| .filter(item => new Date(item.date) > oneWeekAgo) | |
| .reduce((sum, item) => sum + item.duration, 0); | |
| document.getElementById('week-time').textContent = formatTime(weekSeconds); | |
| // Render chart if there's data | |
| if (filteredData.length > 0) { | |
| renderTimeChart(filteredData); | |
| } else if (timeChart) { | |
| timeChart.destroy(); | |
| timeChart = null; | |
| } | |
| } | |
| // Fullscreen functionality | |
| const fullscreenBtn = document.getElementById('fullscreen-btn'); | |
| const timerContainer = document.querySelector('.timer-container'); | |
| fullscreenBtn.addEventListener('click', () => { | |
| if (!document.fullscreenElement) { | |
| timerContainer.requestFullscreen().catch(err => { | |
| alert(`Error attempting to enable fullscreen: ${err.message}`); | |
| }); | |
| } else { | |
| document.exitFullscreen(); | |
| } | |
| }); | |
| // Change fullscreen icon when in fullscreen | |
| document.addEventListener('fullscreenchange', () => { | |
| if (document.fullscreenElement) { | |
| fullscreenBtn.innerHTML = ` | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 20L10 16m0 0v4m0-4H6m12 4l-4-4m4 4v-4m0 4h-4M4 4l4 4m0 0V4m0 4H4m16 0l-4 4m4-4V4m0 4h-4" /> | |
| </svg> | |
| `; | |
| } else { | |
| fullscreenBtn.innerHTML = ` | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" /> | |
| </svg> | |
| `; | |
| } | |
| }); | |
| }); | |
| </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=suhas0/focus-flow-timer" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |