1- import {
2- createPomodoroState ,
3- switchMode ,
4- tickTimer ,
5- getDurationForMode ,
6- formatTime ,
7- } from './core/timer.js' ;
8- import { updateModeButtons } from './ui/render.js' ;
1+ // Centralized timer durations (seconds) for each mode.
2+ export const DURATIONS = {
3+ focus : 25 * 60 ,
4+ 'short-break' : 5 * 60 ,
5+ 'long-break' : 15 * 60 ,
6+ } ;
7+
8+ // ===== Core timer helpers =====
9+ export function createPomodoroState ( ) {
10+ return {
11+ mode : 'focus' ,
12+ remainingSeconds : DURATIONS . focus ,
13+ completedPomodoros : 0 ,
14+ isRunning : false ,
15+ tasks : [ ] ,
16+ } ;
17+ }
18+
19+ export function getDurationForMode ( mode ) {
20+ return DURATIONS [ mode ] || DURATIONS . focus ;
21+ }
22+
23+ export function switchMode ( state , mode ) {
24+ const nextDuration = getDurationForMode ( mode ) ;
25+ return {
26+ ...state ,
27+ mode,
28+ remainingSeconds : nextDuration ,
29+ isRunning : false ,
30+ } ;
31+ }
32+
33+ export function tickTimer ( state ) {
34+ if ( state . remainingSeconds > 1 ) {
35+ return {
36+ nextState : { ...state , remainingSeconds : state . remainingSeconds - 1 } ,
37+ completedCycle : false ,
38+ } ;
39+ }
40+
41+ let completedPomodoros = state . completedPomodoros ;
42+ let nextMode ;
43+
44+ if ( state . mode === 'focus' ) {
45+ completedPomodoros += 1 ;
46+ nextMode = completedPomodoros % 4 === 0 ? 'long-break' : 'short-break' ;
47+ } else {
48+ nextMode = 'focus' ;
49+ }
50+
51+ const nextDuration = getDurationForMode ( nextMode ) ;
52+
53+ return {
54+ nextState : {
55+ ...state ,
56+ mode : nextMode ,
57+ remainingSeconds : nextDuration ,
58+ completedPomodoros,
59+ isRunning : false ,
60+ } ,
61+ completedCycle : true ,
62+ } ;
63+ }
64+
65+ export function formatTime ( totalSeconds ) {
66+ const minutes = Math . floor ( totalSeconds / 60 )
67+ . toString ( )
68+ . padStart ( 2 , '0' ) ;
69+ const seconds = ( totalSeconds % 60 ) . toString ( ) . padStart ( 2 , '0' ) ;
70+ return `${ minutes } :${ seconds } ` ;
71+ }
72+
73+ // ===== UI helpers =====
74+ export function updateModeButtons ( modeButtons , activeMode ) {
75+ for ( const button of modeButtons ) {
76+ if ( button . dataset . mode === activeMode ) {
77+ button . classList . add ( 'active' ) ;
78+ } else {
79+ button . classList . remove ( 'active' ) ;
80+ }
81+ }
82+ }
983
1084/**
1185 * Initializes the Pomodoro application by setting up the UI, state, and event listeners.
@@ -35,13 +109,7 @@ export function initializePomodoroApp(
35109 const iterationCount = doc . getElementById ( 'iteration-count' ) ;
36110
37111 // Ensure all critical UI components are present before proceeding.
38- if (
39- ! timerDisplay ||
40- ! startButton ||
41- ! pauseButton ||
42- ! resetButton ||
43- ! iterationCount
44- ) {
112+ if ( ! timerDisplay || ! startButton || ! pauseButton || ! resetButton ) {
45113 console . error ( 'A critical UI element is missing from the DOM.' ) ;
46114 return ;
47115 }
@@ -72,13 +140,24 @@ export function initializePomodoroApp(
72140 */
73141 function render ( ) {
74142 timerDisplay . textContent = formatTime ( state . remainingSeconds ) ;
75- iterationCount . textContent = `${ state . completedPomodoros } ` ;
143+ if ( iterationCount ) {
144+ iterationCount . textContent = `${ state . completedPomodoros } ` ;
145+ }
76146 // Update the visual state of mode selection buttons.
77147 updateModeButtons ( modeButtons , state . mode ) ;
148+ updateControls ( ) ;
78149 }
79150 // ========= END Live Coding =========
80151
81152 // ========= START Live Coding: Timer Controls (Start/Pause/Reset) =========
153+ /**
154+ * Keeps control buttons in sync with running/paused state for quick visual feedback.
155+ */
156+ function updateControls ( ) {
157+ startButton . disabled = state . isRunning ;
158+ pauseButton . disabled = ! state . isRunning ;
159+ }
160+
82161 /**
83162 * Starts the timer loop.
84163 * If the timer is not already running, it sets up a `setInterval` to tick
@@ -90,6 +169,7 @@ export function initializePomodoroApp(
90169 return ; // Prevent multiple intervals from running simultaneously.
91170 }
92171 state = { ...state , isRunning : true } ;
172+ render ( ) ; // Immediate UI feedback.
93173 intervalId = setInterval ( ( ) => {
94174 const { nextState, completedCycle } = tickTimer ( state ) ;
95175 state = nextState ;
@@ -98,6 +178,7 @@ export function initializePomodoroApp(
98178 // Stop the timer if the session (e.g., focus, break) has ended.
99179 if ( completedCycle ) {
100180 stopTimer ( ) ;
181+ render ( ) ; // Refresh controls after stopping.
101182 }
102183 } , 1000 ) ;
103184 }
@@ -157,5 +238,10 @@ if (globalThis.window !== undefined) {
157238 globalThis . PomodoroApp = {
158239 initializePomodoroApp,
159240 } ;
160- globalThis . addEventListener ( 'DOMContentLoaded' , ( ) => initializePomodoroApp ( ) ) ;
241+ if ( document . readyState === 'loading' ) {
242+ globalThis . addEventListener ( 'DOMContentLoaded' , ( ) => initializePomodoroApp ( ) ) ;
243+ } else {
244+ // If the script loads after DOMContentLoaded, initialize immediately.
245+ initializePomodoroApp ( ) ;
246+ }
161247}
0 commit comments