• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
ozanatgreenpt

ozanatgreenpt

thirdTimer

This version stores some data, so you can restart the app!
Remix of cricks_unmixed4u/thirdTimer
Public
Like
thirdTimer
Home
Code
11
.cursor
1
docs
2
src
3
.cursorrules
.vtignore
AGENTS.md
README.md
article.md
biome.json
deno.json
knowledge.md
Environment variables
Branches
1
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
docs
/
timer-state-and-intents.md
Code
/
docs
/
timer-state-and-intents.md
Search
…
Viewing readonly version of main branch: v129
View latest version
timer-state-and-intents.md

Timer State & Intents

Developer reference for the TimerState shape, state transitions, and session intent lifecycle.

TimerState shape

Defined in src/shared/types.ts:

FieldTypeDescription
mode"idle" | "working" | "breaking"Current timer mode
workStartTimenumber | nullEpoch ms when the current work block began; null when not working
breakStartTimenumber | nullEpoch ms when the current break block began; null when not breaking
savedBreakMinutesnumberAccumulated unused break time (minutes). Grows when work ends, shrinks when break time is consumed
breakFractionnumberRatio of break-to-work (default 1/3). Persists across resets
totalWorkTimenumberCumulative work minutes finalized at mode transitions
totalBreakTimenumberCumulative break minutes finalized at mode transitions
sessionStartedAtnumber | nullEpoch ms when the session first started (set on the first startWork from idle); used as the upsert key for session history

State transitions

All transition functions live in src/frontend/utils/timer-actions.ts. Each returns a TimerActionResult containing the new state and an optional notification.

ActionValid fromMode afterKey field changes
startWorkidle, breakingworkingworkStartTime = now, breakStartTime = null, sessionStartedAt = now (only if was idle)
takeBreakworkingbreakingEarned break = workDuration * breakFraction; savedBreakMinutes += earned; totalWorkTime += workDuration; breakStartTime = now; workStartTime = null
resumeWorkbreakingworkingConsumed break = min(breakDuration, savedBreakMinutes); totalBreakTime += consumed; savedBreakMinutes -= breakDuration (floored at 0); workStartTime = now; breakStartTime = null
handleInterruptionworkingbreakingSame accumulation as takeBreak (earned break added to savedBreakMinutes, totalWorkTime updated). Represents an unplanned break
takeBigBreakworking, breakingbreakingIf was working: totalWorkTime += workDuration. savedBreakMinutes = 0 (all saved break consumed); breakStartTime = now; workStartTime = null
resetSessionanyidleAll timing fields zeroed: workStartTime = null, breakStartTime = null, savedBreakMinutes = 0, totalWorkTime = 0, totalBreakTime = 0, sessionStartedAt = null. breakFraction is preserved

Time accumulation rule

totalWorkTime and totalBreakTime only grow at mode transitions (when an action function is called). They never grow continuously.

While the timer is running, the in-progress block (time since workStartTime or breakStartTime) is not reflected in totalWorkTime/totalBreakTime within the state object. UI components and session-history writers compute this on the fly:

currentWorkTime  = totalWorkTime  + (now - workStartTime)  / 60000   // if mode === "working"
currentBreakTime = totalBreakTime + min((now - breakStartTime) / 60000, savedBreakMinutes)  // if mode === "breaking"

The break cap (min(..., savedBreakMinutes)) ensures break time only counts up to the amount that was earned.

This pattern appears in three places:

  • TimerContainer stats calculation (src/frontend/components/TimerContainer.tsx, stats useMemo)
  • Backend live upsert (src/backend/index.ts, POST /api/state)
  • Frontend reset/rollover (src/frontend/index.tsx, resetSession and day-rollover useEffect)

Intent lifecycle

A session intent is a short user-entered string describing what they plan to work on. It is stored in localStorage, not in TimerState.

Storage

  • Key: timer_session_intent in localStorage
  • Counter: timer_breaks_since_intent tracks how many breaks have occurred since the intent was set
  • Functions: getSessionIntent, setSessionIntent, getBreaksSinceIntent, setBreaksSinceIntent, incrementBreaksSinceIntent in src/frontend/utils/passphrase.ts

Intent changes per user action

User actionEffect on intentbreaksSinceIntent
startWorkSet to input value (or removed if blank)Reset to 0
takeBreakUnchangedIncremented by 1
resumeWorkUnchangedUnchanged
handleInterruptionCleared (null)Reset to 0
takeBigBreakCleared (null)Reset to 0
Clear intent buttonCleared (null)Reset to 0
Submit intent while workingSet to input valueReset to 0
resetSessionNot explicitly clearedNot explicitly reset

After handleInterruption and takeBigBreak, the intent input auto-focuses so the user can set a new intent before resuming work.

The intent is not explicitly cleared on resetSession or day rollover. It persists in localStorage until the next startWork overwrites it or the user clears it manually. In practice, every new work session begins with startWork, which always writes the intent.

Intent in session history

When session history is written (via POST /api/state or POST /api/history), the current sessionIntent from localStorage is included. This means the history record reflects whatever intent was active at write time, not necessarily the intent that was set at session start.

Key source files

  • src/shared/types.ts — TimerState, SessionStats, GroupMemberState type definitions
  • src/frontend/utils/timer-actions.ts — all state transition functions
  • src/frontend/components/TimerContainer.tsx — wires actions to UI, computes live stats
  • src/frontend/utils/passphrase.ts — intent and breaksSinceIntent localStorage helpers
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.