Add event span bars to timeline and anchor view near current time

The timeline rail now shows thick bars indicating event durations —
full opacity for current/future events, dimmed for past. The timeline
range anchors near the current time and extends to show upcoming hours,
capped at midnight.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kate Meerburg 2026-05-24 23:44:04 +02:00
parent 32e9b85857
commit 623d03e843

View file

@ -280,18 +280,32 @@ const TopBar = ({ data }: { data: TodayData }) => {
/* ---------- Agenda ---------- */
const parseTime = (t: string): number => {
const [h, m] = t.split(':').map(Number) as [number, number];
return h * 60 + m;
};
const Agenda = ({ events, weather }: { events: AgendaEvent[]; weather?: WeatherData }) => {
const now = new Date();
const nowStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
const nowMin = now.getHours() * 60 + now.getMinutes();
// Derive timeline range from events
const startMin = events.length
? parseInt(events[0]!.start.split(':')[0]!) * 60
: 8 * 60;
const endMin = events.length
? parseInt(events[events.length - 1]!.end.split(':')[0]!) * 60 + parseInt(events[events.length - 1]!.end.split(':')[1]!)
// Anchor timeline: start ~1h before now (rounded to even hour), end at last event or +8h
const lastEventEnd = events.length
? parseTime(events[events.length - 1]!.end)
: 17 * 60;
const firstEventStart = events.length
? parseTime(events[0]!.start)
: 8 * 60;
const startMin = Math.min(
firstEventStart,
Math.floor((nowMin - 60) / 120) * 120
);
const endMin = Math.min(
24 * 60,
Math.max(lastEventEnd, nowMin + 4 * 60)
);
const rangeMin = Math.max(endMin - startMin, 1);
const pct = Math.min(Math.max((nowMin - startMin) / rangeMin, 0), 1);
@ -304,6 +318,13 @@ const Agenda = ({ events, weather }: { events: AgendaEvent[]; weather?: WeatherD
const startLabel = events[0]?.start ?? '08:00';
const endLabel = events[events.length - 1]?.end ?? '17:00';
// Event spans for thick bars on the timeline rail
const eventSpans = events.map((e) => ({
top: Math.max(0, (parseTime(e.start) - startMin) / rangeMin),
bottom: Math.min(1, (parseTime(e.end) - startMin) / rangeMin),
past: e.end <= nowStr,
}));
return (
<section style={{ padding: '10px 14px 10px 18px', display: 'flex', flexDirection: 'column', minHeight: 0 }}>
<SectionTitle
@ -317,6 +338,22 @@ const Agenda = ({ events, weather }: { events: AgendaEvent[]; weather?: WeatherD
<div style={{ display: 'flex', gap: 10, flex: 1, minHeight: 0 }}>
{/* Timeline rail */}
<div style={{ width: 14, position: 'relative', borderRight: '1px dashed var(--rule)', flex: '0 0 14px' }}>
{/* Event span bars */}
{eventSpans.map((span, i) => (
<div
key={`span-${i}`}
style={{
position: 'absolute',
top: `${span.top * 100}%`,
height: `${(span.bottom - span.top) * 100}%`,
right: -1,
width: 3,
background: 'var(--ink)',
opacity: span.past ? 0.25 : 1,
}}
/>
))}
{/* Hour ticks */}
{ticks.map((h) => {
const p = (h * 60 - startMin) / rangeMin;
return (