Work Session Optimizer

Work Session Optimizer

1. Define Your Session

Override Default Sprint/Break Durations?

2. List Tasks for This Session (Optional)

Default Durations (in minutes)

Reminder Preferences

Optimizing Your Work Sessions: A Quick Guide

1. Define Your Total Session Time
Decide how long you want your entire focused work session to be (e.g., 60 minutes, 90 minutes, 2 hours). Enter this in the "Total Session Duration" field.
2. Configure Sprints & Breaks
Set your desired length for individual "Work Sprints" and "Short Breaks". If it's a longer session, you can also enable and configure "Long Breaks" to occur after a certain number of work sprints. You can use the defaults (from the Settings tab) or override them for the current session.
3. (Optional) List Your Tasks
Before generating the plan, you can list the specific tasks you aim to tackle during this session. This helps you stay oriented when the timer starts. The tool itself doesn't automatically assign these tasks to sprints in this version, but you'll use this list to guide your focus during work intervals.
4. Generate Your Interval Plan
Click "Generate Session Interval Plan". The tool will create a sequence of work sprints and breaks that fit into your total session duration. Review this plan.
5. Start the Session Timer
Click "Start This Session". The timer will begin for the first interval.
  • During "Work Sprints", focus intensely on a task from your list. You can note which task you're on in the "Focusing on" field.
  • During "Breaks", step away, stretch, or rest your eyes.
The tool will alert you when it's time to switch.
Timer Controls During Session:
  • Pause/Resume: For unexpected interruptions.
  • Next Interval: Finish the current work/break interval early and move to the next one in the plan.
  • Stop Session: Ends the entire planned session.
Why Structure Your Sessions?
Techniques like Pomodoro (short sprints with breaks) help maintain high concentration, prevent mental fatigue, and can make large tasks feel less daunting by breaking them into manageable chunks of focused effort.
'; return; } const ul = document.createElement('ul'); ul.style.listStyle = 'none'; ul.style.paddingLeft = '0'; wsoSessionTasksList.forEach(task => { const li = document.createElement('li'); li.className = 'wso-task-item-in-plan'; li.textContent = `${task.name}${task.estimatedTimeMinutes ? ` (${task.estimatedTimeMinutes}m)` : ''}`; const delBtn = document.createElement('button'); delBtn.innerHTML = '×'; delBtn.className = 'wso-button wso-danger'; delBtn.style.padding = '2px 6px'; delBtn.style.fontSize = '0.8em'; delBtn.onclick = () => { wsoSessionTasksList = wsoSessionTasksList.filter(t => t.id !== task.id); wsoRenderSessionTasksList(); }; li.appendChild(delBtn); ul.appendChild(li); }); wsoSessionTasksListEl.appendChild(ul); } // --- Session Plan Generation --- window.wsoGenerateSessionPlan = function() { /* ... from thought process ... */ const totalDurationInput = parseInt(wsoTotalSessionDurationInput.value); if (isNaN(totalDurationInput) || totalDurationInput <= 0) { alert("Please enter a valid total session duration."); return; } const activeConfig = wsoGetActiveSettingsForSession(); const plan = []; let currentTimeElapsed = 0; let workSprintCycleCount = 0; // For long breaks while (currentTimeElapsed < totalDurationInput) { // Add Work Sprint let currentWorkDuration = Math.min(activeConfig.workSprintDuration, totalDurationInput - currentTimeElapsed); if (currentWorkDuration <= 0) break; plan.push({ type: "work", duration: currentWorkDuration, start: currentTimeElapsed, end: currentTimeElapsed + currentWorkDuration }); currentTimeElapsed += currentWorkDuration; workSprintCycleCount++; if (currentTimeElapsed >= totalDurationInput) break; // Add Break let breakDurationToUse; let breakType; if (activeConfig.longBreakEnabled && workSprintCycleCount > 0 && workSprintCycleCount % activeConfig.sprintsBeforeLongBreak === 0) { breakDurationToUse = activeConfig.longBreakDuration; breakType = "long_break"; } else { breakDurationToUse = activeConfig.shortBreakDuration; breakType = "short_break"; } let currentBreakDuration = Math.min(breakDurationToUse, totalDurationInput - currentTimeElapsed); if (currentBreakDuration <= 0) break; plan.push({ type: breakType, duration: currentBreakDuration, start: currentTimeElapsed, end: currentTimeElapsed + currentBreakDuration }); currentTimeElapsed += currentBreakDuration; } wsoCurrentSessionPlan = { totalSessionDurationMinutes: totalDurationInput, planName: wsoSessionNameInput.value.trim() || "Work Session", intervals: plan, generatedAt: new Date().toISOString(), settingsUsed: activeConfig, userTasksForSession: [...wsoSessionTasksList] // Copy current tasks list }; wsoDisplayGeneratedPlanOutput(); if(wsoGeneratedPlanAreaEl) wsoGeneratedPlanAreaEl.style.display = 'block'; if(wsoActiveSessionAreaEl) wsoActiveSessionAreaEl.style.display = 'none'; if(wsoDownloadPdfButton) wsoDownloadPdfButton.style.display = 'block'; } function wsoDisplayGeneratedPlanOutput() { /* ... renders wsoCurrentSessionPlan.intervals ... */ if(!wsoGeneratedPlanOutputEl) return; wsoGeneratedPlanOutputEl.innerHTML = ''; if (!wsoCurrentSessionPlan || wsoCurrentSessionPlan.intervals.length === 0) { wsoGeneratedPlanOutputEl.innerHTML = '

No plan generated. Adjust settings and try again.

'; return; } const ul = document.createElement('ul'); let sessionClock = 0; // To show actual clock times if a start time was implied wsoCurrentSessionPlan.intervals.forEach((interval, index) => { const li = document.createElement('li'); let typeDisplay = interval.type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()); const startTimeDisplay = wsoMinutesToTime(interval.start); const endTimeDisplay = wsoMinutesToTime(interval.end); li.textContent = `${index + 1}. ${typeDisplay} (${interval.duration} min) | From ${startTimeDisplay} to ${endTimeDisplay} of session`; li.classList.add(`plan-${interval.type}`); ul.appendChild(li); }); wsoGeneratedPlanOutputEl.appendChild(ul); } // --- Active Session Timer Logic & UI Update --- // (wsoUpdateTimerDisplay, wsoUpdateActiveSessionUI, wsoStartNextInterval, wsoStartIntervalTimer, // wsoPauseResumeSession, wsoStopSession - similar to Smart Break Reminder but uses wsoCurrentSessionPlan.intervals) // ... Placeholder for these complex functions ... // Placeholder for active timer functions (to be adapted from Smart Break Reminder) function wsoUpdateActiveTimerDisplay() { if(!wsoTimerCountdownDisplayEl || !wsoActiveSessionState.isRunning) return; const minutes = Math.floor(wsoActiveSessionState.timeRemainingInIntervalSeconds / 60); const seconds = wsoActiveSessionState.timeRemainingInIntervalSeconds % 60; wsoTimerCountdownDisplayEl.textContent = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; const currentInterval = wsoCurrentSessionPlan.intervals[wsoActiveSessionState.currentIntervalIndex]; let intervalName = currentInterval.type.replace('_',' ').replace(/\b\w/g, l=>l.toUpperCase()); if (!wsoActiveSessionState.isPaused) { document.title = `${wsoTimerCountdownDisplayEl.textContent} - ${intervalName}`; } else { document.title = `Paused - ${intervalName}`; } } function wsoUpdateActiveSessionView() { if (!wsoCurrentSessionPlan || wsoActiveSessionState.currentIntervalIndex < 0 || wsoActiveSessionState.currentIntervalIndex >= wsoCurrentSessionPlan.intervals.length) { wsoActiveSessionAreaEl.style.display = 'none'; wsoGeneratedPlanAreaEl.style.display = 'block'; // Show plan setup again if(wsoStartSessionButtonEl) wsoStartSessionButtonEl.textContent = "Start New Session"; document.title = "Work Session Optimizer"; return; } wsoActiveSessionAreaEl.style.display = 'block'; wsoGeneratedPlanAreaEl.style.display = 'none'; const currentInterval = wsoCurrentSessionPlan.intervals[wsoActiveSessionState.currentIntervalIndex]; let intervalName = currentInterval.type.replace('_',' ').replace(/\b\w/g, l=>l.toUpperCase()); wsoCurrentIntervalNameDisplayEl.textContent = `${intervalName}`; wsoTimerCountdownDisplayEl.className = currentInterval.type.includes('work') ? 'work-active' : 'break-active'; wsoSessionProgressDisplayEl.textContent = `Interval ${wsoActiveSessionState.currentIntervalIndex + 1} of ${wsoCurrentSessionPlan.intervals.length}`; wsoCurrentTaskInputGroupEl.style.display = currentInterval.type.includes('work') ? 'block' : 'none'; if (wsoActiveSessionState.isPaused) { wsoPauseResumeButtonEl.textContent = "Resume"; } else { wsoPauseResumeButtonEl.textContent = "Pause"; } wsoUpdateActiveTimerDisplay(); } function wsoStartNextActiveInterval(isSkipping = false) { // Log previous interval completion if not skipped or if work was done if (!isSkipping && wsoActiveSessionState.currentIntervalIndex >=0) { const prevInterval = wsoCurrentSessionPlan.intervals[wsoActiveSessionState.currentIntervalIndex]; // Simple log to console for now; PDF/localStorage log could be more detailed console.log(`Finished: ${prevInterval.type} (${prevInterval.duration}m). Task: ${wsoCurrentTaskDescriptionInput.value}`); } wsoActiveSessionState.currentIntervalIndex++; if (wsoActiveSessionState.currentIntervalIndex >= wsoCurrentSessionPlan.intervals.length) { wsoStopActiveSession(true); wsoShowNotification("Session Complete!", "Great job finishing your planned session."); return; } const nextInterval = wsoCurrentSessionPlan.intervals[wsoActiveSessionState.currentIntervalIndex]; wsoActiveSessionState.timeRemainingInIntervalSeconds = nextInterval.duration * 60; wsoActiveSessionState.isPaused = false; wsoCurrentTaskDescriptionInput.value = ""; // Clear for next work sprint let notificationTitle = nextInterval.type.includes('work') ? "Time for Work!" : "Time for a Break!"; let notificationBody = `Starting ${nextInterval.type.replace('_',' ')} for ${nextInterval.duration} minutes.`; wsoShowNotification(notificationTitle, notificationBody); wsoPlayBeep(); wsoRunActiveIntervalTimer(); wsoUpdateActiveSessionView(); } function wsoRunActiveIntervalTimer() { clearInterval(wsoActiveSessionState.timerIntervalId); wsoActiveSessionState.isRunning = true; wsoActiveSessionState.isPaused = false; wsoUpdateActiveSessionView(); wsoActiveSessionState.timerIntervalId = setInterval(() => { if (wsoActiveSessionState.isPaused) return; wsoActiveSessionState.timeRemainingInIntervalSeconds--; wsoUpdateActiveTimerDisplay(); if (wsoActiveSessionState.timeRemainingInIntervalSeconds < 0) { wsoActiveSessionState.timeRemainingInIntervalSeconds = 0; clearInterval(wsoActiveSessionState.timerIntervalId); wsoStartNextActiveInterval(); // Auto-move to next } }, 1000); } function wsoTogglePauseResumeActiveTimer() { if (!wsoActiveSessionState.isRunning) return; wsoActiveSessionState.isPaused = !wsoActiveSessionState.isPaused; if (wsoActiveSessionState.isPaused) { clearInterval(wsoActiveSessionState.timerIntervalId); } else { wsoRunActiveIntervalTimer(); } wsoUpdateActiveSessionView(); } function wsoStopActiveSession(completedNormally = false) { clearInterval(wsoActiveSessionState.timerIntervalId); wsoActiveSessionState.isRunning = false; wsoActiveSessionState.isPaused = false; wsoCurrentIntervalNameDisplayEl.textContent = completedNormally ? "Session Completed!" : "Session Stopped"; wsoTimerCountdownDisplayEl.textContent = "00:00"; wsoTimerCountdownDisplayEl.className = ""; // Reset color wsoSessionProgressDisplayEl.textContent = completedNormally ? "All intervals finished." : "Session ended early."; wsoCurrentTaskInputGroupEl.style.display = 'none'; // Don't nullify wsoCurrentSessionPlan so PDF can still be generated // Reset active state parts for next potential run wsoActiveSessionState.currentIntervalIndex = -1; wsoActiveSessionState.timeRemainingInIntervalSeconds = 0; // Make plan generation visible again wsoActiveSessionAreaEl.style.display = 'none'; wsoGeneratedPlanAreaEl.style.display = 'block'; wsoStartSessionButtonEl.textContent = "Start This Session"; // Or "Start New Session" document.title = "Work Session Optimizer"; } // --- PDF Export --- function handleWsoPdfDownload() { /* ... similar to previous PDF logic, using wsoCurrentSessionPlan ... */ if (!wsoCurrentSessionPlan) { alert("Please generate a session plan first."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const reportDate = new Date().toLocaleDateString(); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--wso-primary-color').trim(); doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text(wsoCurrentSessionPlan.planName || "Work Session Plan", 14, 22); doc.setFontSize(10); doc.setTextColor(100); doc.text(`Total Planned Duration: ${wsoCurrentSessionPlan.totalSessionDurationMinutes} minutes`, 14, 30); const settings = wsoCurrentSessionPlan.settingsUsed; let settingsText = `Config: Work ${settings.workSprintDuration}m, Short Break ${settings.shortBreakDuration}m`; if(settings.longBreakEnabled) { settingsText += `, Long Break ${settings.longBreakDuration}m after ${settings.sprintsBeforeLongBreak} sprints.`; } doc.text(settingsText, 14, 36); doc.text(`Plan Generated: ${new Date(wsoCurrentSessionPlan.generatedAt).toLocaleDateString()}`, 14, 42); let yPos = 50; if (wsoCurrentSessionPlan.userTasksForSession && wsoCurrentSessionPlan.userTasksForSession.length > 0) { doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Intended Tasks for this Session:", 14, yPos); yPos +=7; doc.setFontSize(9); doc.setTextColor(50); wsoCurrentSessionPlan.userTasksForSession.forEach(task => { if (yPos > 270) { doc.addPage(); yPos = 20; } doc.text(`\u2022 ${task.name} ${task.estimatedTimeMinutes ? `(Est: ${task.estimatedTimeMinutes}m)` : ''}`, 16, yPos); yPos += 5; }); yPos += 5; } doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Generated Interval Schedule:", 14, yPos); yPos+=2; const tableColumn = ["#", "Type", "Duration (min)", "Session Start", "Session End"]; const tableRows = []; wsoCurrentSessionPlan.intervals.forEach((interval, index) => { tableRows.push([ index + 1, interval.type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()), interval.duration, wsoMinutesToTime(interval.start), wsoMinutesToTime(interval.end) ]); }); doc.autoTable({ head: [tableColumn], body: tableRows, startY: yPos, theme: 'striped', headStyles: { fillColor: primaryColor } }); doc.save(`${(wsoCurrentSessionPlan.planName || 'WorkSessionPlan').replace(/\s+/g, '_')}.pdf`); } // --- Initialization --- document.addEventListener('DOMContentLoaded', () => { // Assign DOM elements wsoSessionNameInput = document.getElementById('wsoSessionName'); wsoTotalSessionDurationInput = document.getElementById('wsoTotalSessionDuration'); wsoOverrideWorkDurationInput = document.getElementById('wsoOverrideWorkDuration'); wsoOverrideShortBreakDurationInput = document.getElementById('wsoOverrideShortBreakDuration'); wsoOverrideLongBreakEnabledCheckbox = document.getElementById('wsoOverrideLongBreakEnabled'); wsoOverrideLongBreakSettingsEl = document.getElementById('wsoOverrideLongBreakSettings'); wsoOverrideLongBreakDurationInput = document.getElementById('wsoOverrideLongBreakDuration'); wsoOverrideSprintsBeforeLongBreakInput = document.getElementById('wsoOverrideSprintsBeforeLongBreak'); wsoAddTaskToSessionForm = document.getElementById('wsoAddTaskToSessionForm'); wsoSessionTaskNameInput = document.getElementById('wsoSessionTaskNameInput'); wsoSessionTaskEstTimeInput = document.getElementById('wsoSessionTaskEstTimeInput'); wsoSessionTasksListEl = document.getElementById('wsoSessionTasksList'); wsoGeneratedPlanAreaEl = document.getElementById('wsoGeneratedPlanArea'); wsoGeneratedPlanOutputEl = document.getElementById('wsoGeneratedPlanOutput'); wsoStartSessionButtonEl = document.getElementById('wsoStartSessionButton'); wsoActiveSessionAreaEl = document.getElementById('wsoActiveSessionArea'); wsoCurrentIntervalNameDisplayEl = document.getElementById('wsoCurrentIntervalNameDisplay'); wsoTimerCountdownDisplayEl = document.getElementById('wsoTimerCountdownDisplay'); wsoSessionProgressDisplayEl = document.getElementById('wsoSessionProgressDisplay'); wsoCurrentTaskInputGroupEl = document.getElementById('wsoCurrentTaskInputGroup'); wsoCurrentTaskDescriptionInputEl = document.getElementById('wsoCurrentTaskDescription'); wsoPauseResumeButtonEl = document.getElementById('wsoPauseResumeButton'); wsoNextIntervalButtonEl = document.getElementById('wsoNextIntervalButton'); wsoStopSessionButtonEl = document.getElementById('wsoStopSessionButton'); wsoDefaultWorkDurationInput = document.getElementById('wsoDefaultWorkDuration'); wsoDefaultShortBreakDurationInput = document.getElementById('wsoDefaultShortBreakDuration'); wsoDefaultLongBreakEnabledCheckbox = document.getElementById('wsoDefaultLongBreakEnabled'); wsoDefaultLongBreakFieldsEl = document.getElementById('wsoDefaultLongBreakFields'); wsoDefaultLongBreakDurationInput = document.getElementById('wsoDefaultLongBreakDuration'); wsoDefaultSprintsBeforeLongBreakInput = document.getElementById('wsoDefaultSprintsBeforeLongBreak'); wsoSoundAlertsCheckbox = document.getElementById('wsoSoundAlerts'); wsoBrowserNotificationsCheckbox = document.getElementById('wsoBrowserNotifications'); wsoDownloadPdfButton = document.getElementById('wsoDownloadPdfButton'); // Attach event listeners if(wsoDefaultLongBreakEnabledCheckbox) { wsoDefaultLongBreakEnabledCheckbox.addEventListener('change', function() { if(wsoDefaultLongBreakFieldsEl) wsoDefaultLongBreakFieldsEl.style.display = this.checked ? 'block' : 'none'; }); } if(wsoOverrideLongBreakEnabledCheckbox) { wsoOverrideLongBreakEnabledCheckbox.addEventListener('change', function() { if(wsoOverrideLongBreakSettingsEl) wsoOverrideLongBreakSettingsEl.style.display = this.checked ? 'block' : 'none'; if (this.checked) { // Pre-fill with defaults if enabling override wsoOverrideLongBreakDurationInput.value = wsoOverrideLongBreakDurationInput.value || wsoSettings.defaultLongBreakDuration; wsoOverrideSprintsBeforeLongBreakInput.value = wsoOverrideSprintsBeforeLongBreakInput.value || wsoSettings.defaultSprintsBeforeLongBreak; } }); } if(wsoAddTaskToSessionForm) wsoAddTaskToSessionForm.addEventListener('submit', handleAddTaskToSessionFormSubmit); if(wsoStartSessionButtonEl) wsoStartSessionButtonEl.onclick = () => { if (!wsoCurrentSessionPlan) { alert("Please generate a plan first."); return; } wsoActiveSessionState.currentIntervalIndex = -1; wsoActiveSessionState.sessionStartTimeEpoch = Date.now(); // Mark overall session start wsoStartNextActiveInterval(); }; if(wsoPauseResumeButtonEl) wsoPauseResumeButtonEl.onclick = wsoTogglePauseResumeActiveTimer; if(wsoNextIntervalButtonEl) wsoNextIntervalButtonEl.onclick = () => { // Log actual time spent in current interval if ending early? More complex. // For now, just skips to next planned. clearInterval(wsoActiveSessionState.timerIntervalId); wsoStartNextActiveInterval(true); // Pass true if skipping }; if(wsoStopSessionButtonEl) wsoStopSessionButtonEl.onclick = () => wsoStopActiveSession(false); if(wsoDownloadPdfButton) wsoDownloadPdfButton.onclick = handleWsoPdfDownload; wsoLoadGlobalSettings(); // Initialize override placeholders based on loaded defaults if(wsoOverrideWorkDurationInput) wsoOverrideWorkDurationInput.placeholder = `Default: ${wsoSettings.defaultWorkSprintDuration}`; if(wsoOverrideShortBreakDurationInput) wsoOverrideShortBreakDurationInput.placeholder = `Default: ${wsoSettings.defaultShortBreakDuration}`; if(wsoOverrideLongBreakEnabledCheckbox) wsoOverrideLongBreakEnabledCheckbox.checked = wsoSettings.defaultLongBreakEnabled; // Set override checkbox based on default if(wsoOverrideLongBreakSettingsEl) wsoOverrideLongBreakSettingsEl.style.display = wsoSettings.defaultLongBreakEnabled ? 'block' : 'none'; // Show/hide based on default initially if(wsoOverrideLongBreakDurationInput) wsoOverrideLongBreakDurationInput.placeholder = `Default: ${wsoSettings.defaultLongBreakDuration}`; if(wsoOverrideSprintsBeforeLongBreakInput) wsoOverrideSprintsBeforeLongBreakInput.placeholder = `Default: ${wsoSettings.defaultSprintsBeforeLongBreak}`; const firstTabButton = document.querySelector('#workSessionOptimizerTool .wso-tab-button'); if (firstTabButton) firstTabButton.click(); if (wsoBrowserNotificationsCheckbox) { wsoBrowserNotificationsCheckbox.addEventListener('change', function() { wsoSettings.browserNotifications = this.checked; if (this.checked && typeof Notification !== 'undefined' && Notification.permission !== "granted" && Notification.permission !== "denied") { Notification.requestPermission(); } }); } // Initial check for notification permission if already enabled in settings if (wsoSettings.browserNotifications && typeof Notification !== 'undefined' && Notification.permission !== "granted" && Notification.permission !== "denied") { Notification.requestPermission(); } });
Scroll to Top