focus-timer

Focus 🌧️

CI

A motion-first focus web app — Pomodoro timer, reminders, focus tasks and streak tracking, built around a rain-on-glass porthole that clears as you work. One Python process, one SQLite file, no build step, no npm.

Grown from Youth Reminder App, the small Tkinter reminder tool that used to live in this repo. That version is still in the history (and remainder_app_py), and its three quick presets — Study Break, Workout, Chill Time — are still here on the Reminders page.

The timer, light theme

Screenshots

   
Timer, night rain theme Stats
Timer — the pane fogs at the start and clears as the session elapses; a progress arc fills the rim. Stats — today’s focus, day streak, totals, and an animated 7-day chart.

Reminders

Tasks

Stack

Run

python -m venv .venv && .venv/Scripts/python -m pip install -r requirements.txt   # Windows
# (macOS/Linux: source .venv/bin/activate && pip install -r requirements.txt)
.venv/Scripts/python -m uvicorn app:app --reload

Open http://127.0.0.1:8000 — /styleguide shows the design system.

Optional: drop any looping rain recording at static/audio/rain.mp3 for the ambient track. It’s gitignored (a ~24MB binary), and the app is missing-file-safe without it — the switch simply plays nothing.

Test

.venv/Scripts/python -m pytest -q          # 46 tests: pure logic, the HTTP layer, and the CSS guard
python focus.py && python reminders.py     # inline self-checks

tests/test_focus.py and tests/test_reminders.py cover the pure modules; tests/test_api.py drives every route through TestClient against a temp SQLite file; tests/test_css.py guards the theme against a silent CSS parse failure (see Known ceilings).

Features

Only completed focus sessions count toward streaks and totals — breaks and abandoned sessions are stored but excluded from every stat (focus._focus).

Known ceilings

Layout

app.py (routes + API) · db.py (SQLite) · focus.py / reminders.py (pure, tested logic) · templates/ · static/{css,js,vendor,audio} · tests/ · docs/ (README images).

Design tokens live in static/css/tokens.css (HSL custom properties plus the soft-UI and rain/glass primitives); the shared motion vocabulary is static/js/motion.js.