Skip to content

feat: incremental focus ladder + a deeper Better Stats window - #513

Open
gluansinha-star wants to merge 8 commits into
Splode:mainfrom
gluansinha-star:main
Open

gluansinha-star wants to merge 8 commits into
Splode:mainfrom
gluansinha-star:main

Conversation

@gluansinha-star

Copy link
Copy Markdown

What this adds

Two opt-in features on top of upstream Pomotroid 1.7.1. Both are off by default, so the app behaves
exactly like upstream until you turn them on.

1. Incremental Focus

Each completed focus round makes the next one longer, up to a ceiling you choose.

Configured in Settings → Timer → Incremental Focus:

Setting Meaning Default
Incremental Focus Master on/off switch off
Add per Round Minutes added after each completed focus round 5 min
Maximum Focus Ceiling for the escalated duration 90 min

With a 25-minute focus and a 5-minute increment capped at 45 minutes the ladder runs
25 → 30 → 35 → 40 → 45 → 45 → …, and the settings panel renders a live preview of it.

Behaviour:

  • The first round uses the configured Focus duration as-is; the increment starts from round two.
  • The ladder resets after a long break and on Reset, so every cycle starts from the base.
  • Breaks are never escalated — only work rounds grow.
  • A cap below the base duration is clamped up, so a round can never get shorter than the configured
    Focus time.
  • While a work round runs, a caption under the round label shows the position, e.g. 25m +10m · step 3,
    plus · capped once the ceiling is reached.

The ladder lives in SequenceState and reaches the frontend through six new TimerSnapshot fields.
Three new settings persist via MIGRATION_7, so existing databases upgrade without data loss.

2. Better Stats

A new window opened from the titlebar, sitting next to the original Statistics window (which is
untouched and still works exactly as before).

  • Momentum — today vs your trailing 7-day average as a progress ring with a plain-language
    verdict, a streak strip that warns when a live streak has nothing logged today, week-over-week
    comparisons for rounds / focus time / active days / average per active day, and rolling 7, 28 and
    90-day windows.
  • Progress over time — a 28-day area chart with the 7-day moving average overlaid, and a
    day/week toggle; 12-week focus-minutes and rounds bar charts.
  • Consistency — an interactive heatmap with Recent (120 rolling days), Month (a real
    calendar with day numbers, today outlined, future days dashed and ‹ › navigation) and Year
    scopes; a habit-strength donut splitting the last 30 days into strong / light / rest; steadiness
    (standard deviation of daily rounds), best run, your focus window, and a round-length histogram.
  • Rhythm — a weekday × hour grid showing when you actually focus with the peak slot called out,
    plus ranked focus-by-weekday and focus-by-hour breakdowns.
  • Personal bests — longest round, best day by rounds and by focus time, best week, average round
    length, tracked days, streaks.

Everything arrives in a single IPC call (stats_get_insights) so there is no staggered loading, and
charts are hand-rolled SVG with no new dependencies. Durations read naturally: 45m, then 1h 30m,
and whole hours past ten (12h).

Bug fix

While building the stats window I hit a real bug that made several panels render as zeros:

  • day_num_to_date() was shifted by one day, so every date it produced missed every real session.
  • weekday_index() was misaligned by one, skewing the weekday profile and the weekday-by-hour grid.

Together these made fill_recent_days() (the 28-day trend) and roll_up_weeks() (the weekly chart)
build windows containing dates that matched no history. Both are corrected and pinned by tests,
including exact day numbers for known dates and round-trips across leap years, 1970-01-01 and
2100-03-01.

Screenshots

Incremental Focus Better Stats — momentum

| Better Stats — consistency | Better Stats — rhythm |

better-stats Screenshot 2026-09-25 201059 Screenshot 2026-09-25 201245 Screenshot 2026-09-25 201346 Screenshot 2026-09-25 202117

Testing

  • npm run check — 0 errors, 0 warnings.
  • npm run tauri build — produces the NSIS installer and MSI successfully.
  • Manually verified in a running build: the ladder steps correctly and caps, resets after a long
    break and on Reset; the stats window renders all sections and the heatmap navigates across months
    and years.

Not yet run: cargo test. Tests are written for the new ladder logic, the date fix and the new
query helpers, but the suite has not been executed. Worth running before merge.

Notes for reviewers

  • src-tauri/src/db/queries.rs carries most of the new Rust; the insights layer pulls the full
    history once and derives every metric in Rust, which keeps the SQL simple and the snapshot
    consistent.
  • src/lib/dev/mockTauri.js is a dev-only IPC stand-in so the UI can be previewed under plain
    npm run dev in a browser. It is guarded by import.meta.env.DEV and never enters a production
    build, but it is the one addition worth a deliberate look if you'd rather not carry it.
  • The three new message keys per feature were added to all 8 locales with English fallbacks via
    scripts/sync-messages.mjs; real translations are still needed for the non-English locales.

Adds two opt-in features on top of upstream Pomotroid, plus a fix for a
date-arithmetic bug that made several statistics render as zeros.

Incremental Focus
- New settings: incremental_work_enabled, time_work_increment_secs,
  time_work_max_secs (MIGRATION_7 seeds them, so existing DBs upgrade cleanly).
- SequenceState owns the ladder: a work round is lengthened by the increment for
  every work round already completed in the cycle, capped at the ceiling and
  never shorter than the configured base duration. The first round uses the base
  duration as-is, breaks are never escalated, and the ladder resets after a long
  break and on manual Reset.
- TimerSnapshot carries the ladder state to the frontend so the UI can show the
  current step, and Settings -> Timer gains controls plus a live ladder preview.

Better Stats
- New "better-stats" window, opened from the titlebar, alongside the original
  Statistics window which is untouched.
- Backend: stats_get_insights returns everything in one IPC round-trip —
  today's activity, trailing 7/28/90-day windows, week-over-week comparisons,
  a 28-day trend with a 7-day moving average, a 12-week rollup, a 120-day
  heatmap, full daily history, an all-time weekday x hour grid, a session-length
  histogram, 30-day consistency metrics, streaks, and personal bests.
- Frontend: hand-rolled SVG charts (area/line, ring, donut, bars, heatmap,
  weekday x hour grid, ranked bars) in src/lib/components/better-stats.
- Heatmap has Recent / Month / Year scopes; the month view renders a real
  calendar with day numbers and navigation.
- Durations read naturally: 45m, then 1h 30m, and whole hours past ten (12h).

Fixes
- day_num_to_date was shifted by one day and weekday_index was misaligned, so
  the 28-day trend and weekly rollup produced dates that matched no session and
  rendered as zeros. Both are corrected and pinned by tests, along with the
  exact day numbers for known dates.

Tooling
- scripts/seed-demo-data.mjs generates realistic history for reviewing the stats
  windows; scripts/sync-messages.mjs keeps locale files in sync with en.json.
- src/lib/dev/mockTauri.js lets the UI run under plain `npm run dev` in a
  browser by standing in for the Rust backend. Guarded by import.meta.env.DEV,
  so it never ships in a production build.

README documents the fork, both features, and the development helpers.
Adds four screenshots to the README, captured from a running build:

- incremental-focus-settings.png — Settings -> Timer, showing the Incremental
  Focus toggle, the Add per Round and Maximum Focus controls, and the live
  focus-ladder preview.
- better-stats-momentum.png — today's ring against the trailing 7-day average,
  the streak strip, week-over-week comparisons, the rolling window cards, and
  the 28-day trend with its 7-day moving average plus the weekly bar charts.
- better-stats-consistency.png — the 120-day heatmap with its Recent/Month/Year
  scopes, the habit-strength donut and steadiness figures, the focus window,
  and the round-length mix.
- better-stats-rhythm.png — the weekday x hour rhythm grid, focus-by-weekday
  and focus-by-hour breakdowns, and personal bests.

Each image is referenced next to the feature it documents rather than dropped
into a single gallery.

Also adds scripts/capture-screenshots.mjs, which drives Edge over the DevTools
Protocol to capture full-page PNGs at 2x from the running dev server. Its
browser profile is created under the OS temp directory on purpose: keeping it
inside the project tree makes Vite's file watcher fail with EBUSY on the
browser's locked SQLite files. For the same reason .tmp/ is now gitignored.
scripts/publish-release.mjs creates the release on this fork and attaches the
NSIS installer, the MSI and a portable zip built from the release binary. It
reuses the credential Git Credential Manager already stores for github.com and
reads the release body from ../release-notes.md.

Done in Node rather than PowerShell on purpose: ConvertTo-Json serialised the
FileInfo object instead of the release-notes text, so the API rejected the
payload with a 422.

Also documents the script in the development-helpers section.
The ladder used to restart at every long break. Both reset triggers are now
settings, and the ladder is persisted so it survives an app restart:

- incremental_reset_on_long_break: restart when a long break begins
  (default on, so the previous behaviour is unchanged)
- incremental_reset_daily: restart when the local calendar day changes
  (default on)

With both off the ladder keeps climbing until it is reset by hand. A manual
Reset Focus Ladder action (new timer_reset_increment command) drops the ladder
back to the base duration without touching the round, cycle or session
counters; the footer Reset still clears it along with the session.

The day is read through SQLite's date('now','localtime') so it matches the
stats queries, and a rollover only applies while the timer is idle: a round
that is already running keeps the duration it was primed with, and a work
round that finishes just after midnight is credited to the day it started in.

Adds MIGRATION_8 for the two settings, stores the ladder in the settings
key/value table under ladder_steps/ladder_day, reflects it in the settings UI
and the dev mock, and covers it with unit tests.
Second Windows build of the fork: configurable incremental focus ladder
resets on top of the Incremental Focus and Better Stats features.
The bundle directory keeps the installers of earlier releases, so globbing for
the first *-setup.exe could attach the previous version's file. Match on the
version from package.json and fail loudly when it is missing.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant