From 3140a970beac0cd03ad65027071fd01bee489cec Mon Sep 17 00:00:00 2001 From: Duc Manh <51073515+DukeManh@users.noreply.github.com> Date: Sun, 23 Nov 2025 20:54:40 -0500 Subject: [PATCH 1/4] Add starter quick-start button for timer --- css/timer.css | 20 ++++++++++++++++++++ index.html | 1 + js/constants.js | 1 + js/timer.js | 33 ++++++++++++++++++++++++++++++++- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/css/timer.css b/css/timer.css index 20da197..54f7e66 100644 --- a/css/timer.css +++ b/css/timer.css @@ -37,6 +37,7 @@ display: flex; justify-content: center; gap: 12px; + flex-wrap: wrap; margin-bottom: 16px; } @@ -63,6 +64,25 @@ cursor: not-allowed; } +#starterBtn { + padding: 6px 10px; + font-size: 0.85rem; + border: 1px dashed var(--muted); + background: transparent; + color: var(--muted); + transition: color var(--transition), border-color var(--transition), transform var(--transition); +} + +#starterBtn:hover:not(:disabled) { + color: var(--accent); + border-color: var(--accent); + transform: translateY(-1px); +} + +#starterBtn:active:not(:disabled) { + transform: translateY(0); +} + /* Sound notification control styles */ #soundSettings { margin-top: 12px; diff --git a/index.html b/index.html index 17de0ec..d58347f 100644 --- a/index.html +++ b/index.html @@ -186,6 +186,7 @@

52/17 RuleFocus 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..f30b37b 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) { @@ -31,6 +35,12 @@ export function initTimer() { timerCore = new TimerCore({ // Set up callbacks onSessionEnd: recordSession, + onBreakEnd: () => { + if (pendingPresetRestore && timerCore) { + updateTimerPresetWithoutInterruption(pendingPresetRestore); + pendingPresetRestore = null; + } + }, getTodos: getTodos, // Add a custom UI update callback to handle the timer title updates updateUI: (state) => { @@ -56,6 +66,24 @@ export function initTimer() { addTimeBtn: addTimeBtn, timerLabel: document.getElementById('timerLabel') }); + + // Handle quick starter session + starterBtn?.addEventListener('click', () => { + if (!timerCore) 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 +170,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); From 8881f68e4c57b4e2813eee345514608992fbfc3b Mon Sep 17 00:00:00 2001 From: Duc Manh <51073515+DukeManh@users.noreply.github.com> Date: Sun, 23 Nov 2025 21:04:18 -0500 Subject: [PATCH 2/4] Adjust start control labels and starter availability --- index.html | 2 +- js/timer.js | 3 ++- js/timerCore.js | 6 +++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index d58347f..81c4c13 100644 --- a/index.html +++ b/index.html @@ -187,7 +187,7 @@

52/17 Rule52:00

- + diff --git a/js/timer.js b/js/timer.js index f30b37b..eff0e6c 100644 --- a/js/timer.js +++ b/js/timer.js @@ -64,12 +64,13 @@ export function initTimer() { endBtn: endBtn, resetBtn: resetBtn, addTimeBtn: addTimeBtn, + starterBtn: starterBtn, timerLabel: document.getElementById('timerLabel') }); // Handle quick starter session starterBtn?.addEventListener('click', () => { - if (!timerCore) return; + if (!timerCore || timerCore.state.isRunning) return; pendingPresetRestore = timerCore.state.currentPreset !== 'starter' ? timerCore.state.currentPreset diff --git a/js/timerCore.js b/js/timerCore.js index bdf485c..cf16673 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) { From 6d5c34437a2eb792321bfaec00d8aee4f3b78446 Mon Sep 17 00:00:00 2001 From: Duc Manh <51073515+DukeManh@users.noreply.github.com> Date: Sun, 23 Nov 2025 21:07:21 -0500 Subject: [PATCH 3/4] Auto restart selected preset after starter session --- js/timer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/timer.js b/js/timer.js index eff0e6c..e165482 100644 --- a/js/timer.js +++ b/js/timer.js @@ -37,8 +37,11 @@ export function initTimer() { onSessionEnd: recordSession, onBreakEnd: () => { if (pendingPresetRestore && timerCore) { - updateTimerPresetWithoutInterruption(pendingPresetRestore); + const presetToRestore = pendingPresetRestore; pendingPresetRestore = null; + + updateTimerPreset(presetToRestore); + timerCore.start(); } }, getTodos: getTodos, From c658f59492af6ac4374dfc868b6c3b7eee340593 Mon Sep 17 00:00:00 2001 From: Duc Manh <51073515+DukeManh@users.noreply.github.com> Date: Sun, 23 Nov 2025 21:31:10 -0500 Subject: [PATCH 4/4] Skip break after starter session --- js/timer.js | 25 ++++++++++++++++++++++++- js/timerCore.js | 12 +++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/js/timer.js b/js/timer.js index e165482..99a678b 100644 --- a/js/timer.js +++ b/js/timer.js @@ -34,7 +34,30 @@ 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; diff --git a/js/timerCore.js b/js/timerCore.js index cf16673..55be2aa 100644 --- a/js/timerCore.js +++ b/js/timerCore.js @@ -399,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();