Focus Time
52:00
-
+
+
diff --git a/js/constants.js b/js/constants.js
index 5df32e0..bdacfbd 100644
--- a/js/constants.js
+++ b/js/constants.js
@@ -5,6 +5,7 @@ export const TIMER_PRESETS = {
default: { name: '52/17 (Recommended)', work: 52 * 60, break: 17 * 60 },
pomodoro: { name: '25/5 (Pomodoro)', work: 25 * 60, break: 5 * 60 },
deepWork: { name: '90/20 (Deep Work)', work: 90 * 60, break: 20 * 60 },
+ starter: { name: '5/5 (Starter)', work: 5 * 60, break: 5 * 60 },
custom: { name: 'Custom', work: 25 * 60, break: 5 * 60 } // Default values for custom preset
};
diff --git a/js/timer.js b/js/timer.js
index 8d94f37..99a678b 100644
--- a/js/timer.js
+++ b/js/timer.js
@@ -10,7 +10,10 @@ let timerCore;
// Timer elements
let timerEl, progressEl;
-let startBtn, pauseBtn, endBtn, resetBtn, addTimeBtn;
+let startBtn, pauseBtn, endBtn, resetBtn, addTimeBtn, starterBtn;
+
+// Track preset to restore after starter sessions
+let pendingPresetRestore = null;
export function initTimer() {
// Get DOM elements
@@ -21,6 +24,7 @@ export function initTimer() {
endBtn = document.getElementById('endBtn');
resetBtn = document.getElementById('resetBtn');
addTimeBtn = document.getElementById('addTimeBtn');
+ starterBtn = document.getElementById('starterBtn');
// Show initial value while timer is initializing
if (timerEl) {
@@ -30,7 +34,39 @@ export function initTimer() {
// Initialize timer core
timerCore = new TimerCore({
// Set up callbacks
- onSessionEnd: recordSession,
+ onSessionEnd: (session) => {
+ recordSession(session);
+
+ // Skip break after a starter run and immediately restore the user's preset
+ if (timerCore?.state.currentPreset === 'starter' && pendingPresetRestore) {
+ const presetToRestore = pendingPresetRestore;
+ pendingPresetRestore = null;
+
+ // Restore the preset so durations reset correctly
+ updateTimerPreset(presetToRestore);
+
+ // Ensure we remain in focus mode and start fresh
+ timerCore.state.onBreak = false;
+ timerCore.state.remainingTime = timerCore.state.workDuration;
+ timerCore.updateBreakUI();
+ timerCore.updateDisplay();
+
+ // Immediately begin the restored focus session
+ timerCore.start();
+ return { skipBreak: true };
+ }
+
+ return null;
+ },
+ onBreakEnd: () => {
+ if (pendingPresetRestore && timerCore) {
+ const presetToRestore = pendingPresetRestore;
+ pendingPresetRestore = null;
+
+ updateTimerPreset(presetToRestore);
+ timerCore.start();
+ }
+ },
getTodos: getTodos,
// Add a custom UI update callback to handle the timer title updates
updateUI: (state) => {
@@ -54,8 +90,27 @@ export function initTimer() {
endBtn: endBtn,
resetBtn: resetBtn,
addTimeBtn: addTimeBtn,
+ starterBtn: starterBtn,
timerLabel: document.getElementById('timerLabel')
});
+
+ // Handle quick starter session
+ starterBtn?.addEventListener('click', () => {
+ if (!timerCore || timerCore.state.isRunning) return;
+
+ pendingPresetRestore = timerCore.state.currentPreset !== 'starter'
+ ? timerCore.state.currentPreset
+ : null;
+
+ updateTimerPreset('starter');
+
+ // Ensure we start from focus mode with the new preset
+ timerCore.state.onBreak = false;
+ timerCore.state.remainingTime = timerCore.state.workDuration;
+ timerCore.updateBreakUI();
+ timerCore.updateDisplay();
+ timerCore.start();
+ });
}
// Update break auto-start preference
@@ -142,6 +197,9 @@ function updateTimerTitle(presetKey) {
case 'deepWork':
titleElement.innerHTML = ' 90/20 Deep Work';
break;
+ case 'starter':
+ titleElement.innerHTML = ' 5/5 Starter';
+ break;
case 'custom':
// Get the custom preset values to display in the title
const workMinutes = Math.floor(TIMER_PRESETS.custom.work / 60);
diff --git a/js/timerCore.js b/js/timerCore.js
index bdf485c..55be2aa 100644
--- a/js/timerCore.js
+++ b/js/timerCore.js
@@ -192,6 +192,10 @@ export class TimerCore {
this.state.isRunning = running;
+ if (this.elements.starterBtn) {
+ this.elements.starterBtn.disabled = running;
+ }
+
if (this.state.onBreak) {
if (this.elements.startBtn) {
this.elements.startBtn.disabled = running;
@@ -221,7 +225,7 @@ export class TimerCore {
} else {
if (this.elements.startBtn) {
this.elements.startBtn.disabled = running;
- this.elements.startBtn.textContent = "Lock In";
+ this.elements.startBtn.textContent = "Start";
}
if (this.elements.pauseBtn) {
@@ -395,14 +399,24 @@ export class TimerCore {
this.endBreak();
} else {
// Work session ended
+ // Mark timer as stopped before running callbacks or scheduling next steps
+ this.state.isRunning = false;
+
if (this.callbacks.onSessionEnd) {
const sessionDuration = this.state.workDuration;
- this.callbacks.onSessionEnd({
+ const sessionResult = this.callbacks.onSessionEnd({
startTime: this.state.startTime,
duration: sessionDuration,
isBreak: false,
todos: this.callbacks.getTodos ? this.callbacks.getTodos() : []
});
+
+ // Allow consumer to skip break flow after a session
+ if (sessionResult && sessionResult.skipBreak) {
+ this.updateControls(false);
+ this.saveState();
+ return;
+ }
}
playSound(getEndSound());
this.startBreak();