Online Work Session Focus Enhancer

Online Work Session Focus Enhancer

Use a structured timer and ambient sounds to boost your concentration.

25:00 Time to Focus

Daily Goal Progress

0 / 4 Sessions Completed

No sessions logged yet.

`; return; } container.innerHTML = sessionLogs.map(log => { const d = new Date(log.date); return `
${d.toLocaleDateString()} ${d.toLocaleTimeString()} - ${log.duration} min focus
`; }).join(''); } // --- CHART LOGIC --- function updateWeeklyChart() { const ctx = document.getElementById('weeklyFocusChart')?.getContext('2d'); if (!ctx) return; const weeklyData = [0, 0, 0, 0, 0, 0, 0]; // Sun - Sat const today = new Date(); const firstDayOfWeek = new Date(today.setDate(today.getDate() - today.getDay())); sessionLogs.forEach(log => { const logDate = new Date(log.date); const diffDays = Math.floor((logDate - firstDayOfWeek) / (1000 * 60 * 60 * 24)); if (diffDays >= 0 && diffDays < 7) { weeklyData[logDate.getDay()] += log.duration / 60; // in hours } }); if (weeklyFocusChartInstance) weeklyFocusChartInstance.destroy(); weeklyFocusChartInstance = new Chart(ctx, { type: 'bar', data: { labels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], datasets: [{ label: 'Focus Hours', data: weeklyData, backgroundColor: '#14b8a6', borderRadius: 4 }] }, options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, title: { display: true, text: 'Hours' } } } } }); } // --- SOUND GENERATION (WEB AUDIO API) --- function initAudio() { if (!audioCtx) { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } } function playNotificationSound() { initAudio(); const oscillator = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(880, audioCtx.currentTime); gainNode.gain.setValueAtTime(0.2, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.0001, audioCtx.currentTime + 0.5); oscillator.start(); oscillator.stop(audioCtx.currentTime + 0.5); } function startAmbientSound() { const soundType = ambientSoundSelect.value; if (soundType === 'none' || soundNode) return; initAudio(); if (soundType === 'whiteNoise') { const bufferSize = 2 * audioCtx.sampleRate; const noiseBuffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate); const output = noiseBuffer.getChannelData(0); for (let i = 0; i < bufferSize; i++) { output[i] = Math.random() * 2 - 1; } soundNode = audioCtx.createBufferSource(); soundNode.buffer = noiseBuffer; soundNode.loop = true; } else if (soundType === 'rain') { // Simplified rain sound - more complex synthesis is possible const bufferSize = audioCtx.sampleRate * 2; const noiseBuffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate); const output = noiseBuffer.getChannelData(0); for (let i = 0; i < bufferSize; i++) { output[i] = Math.random() * 2 - 1; } const source = audioCtx.createBufferSource(); source.buffer = noiseBuffer; source.loop = true; const filter = audioCtx.createBiquadFilter(); filter.type = 'lowpass'; filter.frequency.value = 800; source.connect(filter); soundNode = filter; // The filter is now the node to connect source.start(0); } const gainNode = audioCtx.createGain(); gainNode.gain.setValueAtTime(0.05, audioCtx.currentTime); soundNode.connect(gainNode).connect(audioCtx.destination); if (soundNode.start) soundNode.start(0); } function stopAmbientSound() { if (soundNode) { if (soundNode.stop) soundNode.stop(0); soundNode.disconnect(); soundNode = null; } } // --- PDF GENERATION --- async function generatePdf() { const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' }); doc.setFont("helvetica", "bold"); doc.setFontSize(22); doc.setTextColor("#0f766e"); doc.text("Focus Session Report", doc.internal.pageSize.width / 2, 60, { align: 'center' }); const totalHours = sessionLogs.reduce((sum, log) => sum + log.duration, 0) / 60; doc.setFont("helvetica", "normal"); doc.setFontSize(12); doc.setTextColor("#111827"); doc.text(`Total Focus Time Logged: ${totalHours.toFixed(2)} hours`, 40, 100); const chartCanvas = document.getElementById('weeklyFocusChart'); const chartImg = chartCanvas.toDataURL('image/png', 1.0); doc.setFontSize(16); doc.text("Weekly Focus Breakdown", 40, 140); doc.addImage(chartImg, 'PNG', 40, 160, 500, 250); const tableData = sessionLogs.map(log => { const d = new Date(log.date); return [ d.toLocaleDateString(), d.toLocaleTimeString(), `${log.duration} minutes` ]; }); doc.autoTable({ startY: 440, head: [['Date', 'Time', 'Duration']], body: tableData, theme: 'grid', headStyles: { fillColor: '#0d9488' } }); doc.save(`Focus-Report_${new Date().toISOString().split('T')[0]}.pdf`); } // --- TAB NAVIGATION LOGIC --- function switchTab(tabIndex) { currentTabIndex = tabIndex; tabsContainer.querySelectorAll('button').forEach((btn, index) => { btn.classList.toggle('tab-active', index === tabIndex); btn.classList.toggle('tab-inactive', index !== tabIndex); }); tabContents.forEach((content, index) => { content.classList.toggle('hidden', index !== tabIndex); }); updateButtonStates(); // Refresh UI elements on tab switch if (tabs[tabIndex] === 'sessionLog') { renderSessionLog(); updateWeeklyChart(); } } function updateButtonStates() { prevBtn.disabled = currentTabIndex === 0; nextBtn.disabled = currentTabIndex === tabs.length - 1; prevBtn.classList.toggle('opacity-50', prevBtn.disabled); nextBtn.classList.toggle('opacity-50', nextBtn.disabled); } // --- EVENT LISTENERS --- startPauseBtn.addEventListener('click', toggleTimer); resetBtn.addEventListener('click', () => resetTimer()); saveSettingsBtn.addEventListener('click', saveSettings); downloadPdfBtn.addEventListener('click', generatePdf); ambientSoundSelect.addEventListener('change', () => { stopAmbientSound(); if (state.isRunning) startAmbientSound(); }); tabsContainer.querySelectorAll('button').forEach((btn, index) => { btn.addEventListener('click', () => switchTab(index)); }); prevBtn.addEventListener('click', () => { if (currentTabIndex > 0) switchTab(currentTabIndex - 1); }); nextBtn.addEventListener('click', () => { if (currentTabIndex < tabs.length - 1) switchTab(currentTabIndex + 1); }); // --- RUN INITIALIZATION --- initialize(); });
Scroll to Top