Mark now on the day-long charts

The hour heatmap and the two actograms all run midnight to midnight, and none
of them said where in that span you currently are. On the heatmap that made the
darkest block ambiguous: a column at 18h is either a habit or simply the hour
you are standing in with the rest of the day not yet lived. On the actograms it
hid the reason today's row stops early — it stops at now, not at bedtime.

One rule serves all three. drawActogram already draws both timelines, so the
line lands there once, and the heatmap appends the same parts after its hit
rects. Ink over a surface-coloured underlay, the same treatment the timing
chart's "since the last one" marker uses, so it stays readable crossing a
shaded sleep window, a dark cell or bare track. A caret hangs above the track
to keep it from reading as one more hour gridline, and the whole thing is
pointer-events: none so it cannot swallow a tap meant for the row underneath.

Deliberately not the accent colour: on these charts accent already means "the
selected day", and now is a different kind of fact.

The captions gain a clause naming the marker, which runs against the last few
commits taking captions away. Those removed lines restated what the picture
already said. This one does not: a thin vertical line has no inherent meaning,
and unlike darker-means-more there is nothing to infer it from.
This commit is contained in:
Alexander Heldt
2026-09-04 15:11:30 +00:00
parent 122897c732
commit 103a5f9937
4 changed files with 38 additions and 4 deletions
+27 -1
View File
@@ -1587,6 +1587,28 @@
// ---------- pattern charts ----------
// Where "now" falls in the day, 0..1 — the shared x for the charts that run
// midnight to midnight.
function nowDayFraction() {
const now = new Date();
return (now.getTime() - startOfDay(now).getTime()) / 86_400_000;
}
// Vertical "now" rule for those charts: ink over a surface-coloured underlay
// so it stays legible whatever bar or cell it crosses, with a caret hung
// above the track so it doesn't read as one more gridline. Append the parts
// last so the rule sits on top; it ignores pointer events, so the rows and
// cells underneath stay tappable.
function nowRuleParts(x, yTop, yBottom) {
const xs = x.toFixed(1);
return [
`<line class="now-rule-ring" x1="${xs}" y1="${yTop}" x2="${xs}" y2="${yBottom}"/>`,
`<line class="now-rule" x1="${xs}" y1="${yTop}" x2="${xs}" y2="${yBottom}"/>`,
`<path class="now-rule-cap" d="M${(x - 3).toFixed(1)} ${yTop - 4} ` +
`L${(x + 3).toFixed(1)} ${yTop - 4} L${xs} ${yTop} Z"/>`,
];
}
// Actogram: one row per day (oldest at top), a midnight-to-midnight track
// with the given windows shaded. Windows are clipped to each day, so one that
// crosses midnight shows correctly split across two rows, and an open one
@@ -1647,6 +1669,10 @@
parts.push(`<rect class="bar stl-hit" data-day="${ymd(day)}" x="${ML}" y="${y.toFixed(1)}" width="${innerW}" height="${rowH.toFixed(1)}"><title>${escapeText(title)}</title></rect>`);
}
// The window always ends today, so one rule marks both the live edge of the
// bottom row and the same time of day on every row above it.
parts.push(...nowRuleParts(xOf(nowDayFraction()), MT, H - MB));
setChartSVG(svg, parts); // wires the .bar[data-day] click → select that day
}
@@ -1894,7 +1920,7 @@
}
parts.push(`<text x="${(ML + innerW).toFixed(1)}" y="${yAxis}" text-anchor="end">24h</text>`);
svg.innerHTML = parts.concat(hits).join("");
svg.innerHTML = parts.concat(hits, nowRuleParts(ML + nowDayFraction() * innerW, MT, H - MB)).join("");
// <title> tooltips only ever show on a pointer, which left phones with no
// way to read a block's count. Tapping (or hovering) a block names it in