The configured bedtime window: whether the feature is on, its
start/end minute-of-day bounds, and the per-window re-prompt interval.
Grouped so decide_bedtime’s call sites set each field by name rather
than passing four positional values that are easy to transpose.
The four conditions that gate a pre-break warning before any timing
is considered: the feature must be on, the schedule must include
interval breaks, the user must not be idle-suppressed, and we must not
have already warned this cycle. Grouped so each is set by name.
Decision returned by decide_bedtime — fully captures what the tick
should do with the bedtime window. The caller still performs the
side effects (overlay, hooks, logging, timer mutation).
Clear the “resume last skipped break” slot. Returns true iff a
stored break was actually cleared (used to decide whether to emit
the last_break:changed event).
Minutes since local midnight (0..1440). The unit used everywhere
the scheduler reasons about time-of-day windows (work hours,
bedtime window, fixed-time break list).
The current local weekday as days-since-Monday (0 = Monday … 6 =
Sunday). Matches the bit layout of Settings::work_days_mask so the
work-window day check can index it directly.
True iff now (minutes since midnight) falls inside [start, end),
with wrap-around: a window like 22:00–06:00 correctly straddles
midnight. start == end is treated as an empty window.
Parse "HH:MM" (or "H:MM") into minutes since midnight.
Returns None on anything out of range or unparseable — used to
filter the user’s fixed-time list without spilling errors.
True iff the pre-break notification for this kind should fire now —
i.e. we’re inside the lead window before a due interval break, and
we haven’t already shown the notification for this cycle.
Re-anchor the interval clocks when the scheduler resumes from a pause
(timed-pause expiry or a manual “Resume”). Paused time must not count
toward break intervals: without this, an hour-long pause leaves
last_micro / last_long an hour in the past, so the very next tick
sees every interval already overdue — the tray reads 0:00 and a
break fires the instant the user resumes (#134).
Re-anchor the micro / long interval clocks to now and clear the
deferral / postpone / warn state, without disturbing last_sleep,
active_break, or the fixed-time dedupe keys. now is injected so
the re-anchor is a pure function over BreakTimers and a timestamp.
Apply the timer bookkeeping for a scheduled micro/long break that has
just fired. Pure analogue of the inline resets in run_loop’s
fixed-time and interval fire paths, so the field-by-field divergence
between the two break kinds (and between a fixed vs interval fire) is
unit-testable without a windowing runtime or a live clock.
Reset the micro / long timers and clear deferral / postpone state
without disturbing last_sleep or active_break. Called when the
active profile switches: a new profile gets fresh intervals but we
don’t want to re-fire a sleep prompt that’s already shown today.
Decide whether a due break should be delayed because the user is
mid-keystroke. Returns true while we should keep waiting and
false once either the user has paused typing OR the deferral cap
has been reached (so we don’t postpone indefinitely).
Dedupe gate for fixed-time fires: true unless we already fired
this exact (date, minute) slot. Prevents the 1Hz tick from firing
the same fixed slot up to 60 times, and (because the key includes
the local date) stays correct across DST: 02:00 on a “fall back”
day fires once even though the wall clock visits it twice, and
02:30 on a “spring forward” day simply never matches.
True iff the work window is open right now: the time-of-day is inside
[start, end)and the relevant weekday’s bit is set in
days_mask. today is days-since-Monday (0=Mon … 6=Sun), the
same layout as the mask.