Make the Sleep trend chart follow the selected day

The curves were always computed relative to today regardless of the day
picker. Anchor them to selectedDay() instead: a past day shows its full
24h curve against the day before it and the average of the days leading
up to it, with no now-cap and no projection. Legend and tooltip labels
show the actual dates when viewing a past day.
This commit is contained in:
Alexander Heldt
2026-07-23 08:45:26 +00:00
parent 0ebaa11c93
commit 22a5ea76aa
3 changed files with 25 additions and 13 deletions
+23 -12
View File
@@ -1278,9 +1278,10 @@
}
// ---------- sleep trend ----------
// Cumulative hours slept as the day progresses: today's running total (up to
// now) against yesterday's full curve and the mean of the last N full days,
// where N follows the 7/14/30 chart-days picker.
// Cumulative hours slept as the selected day progresses: that day's running
// total (up to now when it's today, else the full 24h) against the previous
// day's full curve and the mean of the N full days before it, where N
// follows the 7/14/30 chart-days picker.
// Each curve is a list of { x: hour-of-day 0..24, y: cumulative hours }.
function sleepTrendCurves(events) {
const HOUR = 3_600_000;
@@ -1294,8 +1295,10 @@
}
return total;
};
const day = selectedDay();
const isToday = ymd(day) === ymd(new Date());
const dayStartTs = (daysAgo) => {
const d = startOfDay(new Date());
const d = startOfDay(day);
d.setDate(d.getDate() - daysAgo);
return d.getTime();
};
@@ -1314,7 +1317,8 @@
return pts;
};
const today = curveFor(dayStartTs(0), Date.now());
// A past day is complete, so its curve runs the full 24h uncapped.
const today = curveFor(dayStartTs(0), isToday ? Date.now() : null);
const yesterdayCurve = curveFor(dayStartTs(1));
const yesterday = yesterdayCurve[24].y > 0 ? yesterdayCurve : null;
@@ -1339,9 +1343,10 @@
// the average day typically adds between now and midnight. That respects
// the time-of-day rhythm (night sleep, nap clusters), unlike a linear
// rate extrapolation, which overshoots wildly just after a long night.
// No history → no average → no projection.
// No history → no average → no projection. Past days are already complete,
// so there is nothing to project.
let projected = null;
if (avg) {
if (avg && isToday) {
const nowPt = today[today.length - 1];
const avgAt = (x) => {
const lo = Math.floor(x);
@@ -1355,7 +1360,13 @@
}
}
return { today, yesterday, avg, avgDays, projected };
// Labels follow the selection so a past day reads as its date, not "Today".
const fmtDay = (daysAgo) =>
new Date(dayStartTs(daysAgo)).toLocaleDateString(undefined, { month: "short", day: "numeric" });
const dayLabel = isToday ? "Today" : fmtDay(0);
const prevDayLabel = isToday ? "Yesterday" : fmtDay(1);
return { today, yesterday, avg, avgDays, projected, dayLabel, prevDayLabel };
}
function drawSleepTrendChart(curves, target) {
@@ -1369,9 +1380,9 @@
const series = [
// Reference lines first so today (and its projected tail) draw on top.
{ pts: curves.avg, cls: "trend-avg", label: `${curves.avgDays}-day average` },
{ pts: curves.yesterday, cls: "trend-yesterday", label: "Yesterday" },
{ pts: curves.yesterday, cls: "trend-yesterday", label: curves.prevDayLabel },
{ pts: curves.projected, cls: "trend-projected", label: "Projected end of day" },
{ pts: curves.today, cls: "trend-today", label: "Today" },
{ pts: curves.today, cls: "trend-today", label: curves.dayLabel },
].filter(s => s.pts && s.pts.length > 1);
// The axis must reach the goal band's top even when the data doesn't yet.
@@ -1452,11 +1463,11 @@
// write each curve's slept-hours total into its chip.
const chip = (id) => document.getElementById(id);
const hrs = (pts) => `${pts[pts.length - 1].y.toFixed(1)}h`;
chip("legend-trend-today-text").textContent = `Today ${hrs(curves.today)}`;
chip("legend-trend-today-text").textContent = `${curves.dayLabel} ${hrs(curves.today)}`;
const yLegend = chip("legend-trend-yesterday");
yLegend.hidden = !curves.yesterday;
if (curves.yesterday) {
chip("legend-trend-yesterday-text").textContent = `Yesterday ${hrs(curves.yesterday)}`;
chip("legend-trend-yesterday-text").textContent = `${curves.prevDayLabel} ${hrs(curves.yesterday)}`;
}
const aLegend = chip("legend-trend-avg");
aLegend.hidden = !curves.avg;