From e750cfe9dd6a205fe8859d6dd83145eefd510653 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Tue, 18 Aug 2026 10:39:36 +0200 Subject: [PATCH] feat(web): gap badge on timeline rows after multi-hour/day time gaps Timeline rows render time-of-day only, so a large gap between adjacent events (and the date change it implies) was invisible without expanding event details. Rows following a gap >= 1h now show an amber chip (e.g. "+3h" / "+19d"); hovering reveals the full date. Applies to both the phase timeline and raw mode, using the same display timestamp as the row (backfilled events keep their event-log time). Co-Authored-By: Claude Fable 5 --- .../app/sessions/components/EventTimeline.tsx | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/Web/autopilot-monitor-web/app/sessions/components/EventTimeline.tsx b/src/Web/autopilot-monitor-web/app/sessions/components/EventTimeline.tsx index 05f49156..497e435c 100644 --- a/src/Web/autopilot-monitor-web/app/sessions/components/EventTimeline.tsx +++ b/src/Web/autopilot-monitor-web/app/sessions/components/EventTimeline.tsx @@ -180,8 +180,12 @@ export default function EventTimeline({

Raw Events ({sortedBySequence.length})

- {sortedBySequence.map((ev) => ( - + {sortedBySequence.map((ev, i) => ( + 0 ? getDisplayTime(sortedBySequence[i - 1]) : null} + /> ))}
@@ -341,11 +345,12 @@ function PhaseSection({ {isExpanded && (
- {events.map((event) => ( + {events.map((event, i) => ( 0 ? getDisplayTime(events[i - 1]) : null} /> ))}
@@ -372,7 +377,32 @@ const BACKFILL_TOOLTIP = "If enrollment progressed afterwards, this error was already resolved by a retry. " + "The timestamp shown is the original event-log time."; -function EventRow({ event, showScriptOutput }: { event: EnrollmentEvent; showScriptOutput?: boolean }) { +// Display timestamp of a row: backfilled events show the original event-log time. +function getDisplayTime(event: EnrollmentEvent): Date { + return getBackfillInfo(event).recordedAt ?? new Date(event.timestamp); +} + +// Rows render time-of-day only, so a multi-hour/multi-day gap to the previous row +// (and the date change it implies) is invisible without expanding event details. +// Shown on the row AFTER the gap; hover reveals the full date. +function GapBadge({ prevTime, eventTime }: { prevTime: Date | null; eventTime: Date }) { + if (!prevTime) return null; + const gapMs = eventTime.getTime() - prevTime.getTime(); + if (gapMs < 60 * 60 * 1000) return null; + const label = gapMs >= 48 * 60 * 60 * 1000 + ? `+${Math.round(gapMs / (24 * 60 * 60 * 1000))}d` + : `+${Math.round(gapMs / (60 * 60 * 1000))}h`; + return ( + + ⏱ {label} + + ); +} + +function EventRow({ event, showScriptOutput, prevDisplayTime }: { event: EnrollmentEvent; showScriptOutput?: boolean; prevDisplayTime?: Date | null }) { const [showDetails, setShowDetails] = useState(false); const [showRaw, setShowRaw] = useState(false); const [copied, setCopied] = useState(false); @@ -458,6 +488,7 @@ function EventRow({ event, showScriptOutput }: { event: EnrollmentEvent; showScr (reported {new Date(event.timestamp).toLocaleTimeString()}) )} + {isBackfilled && ( normalizeEventDataForDisplay(event.data), [event.data]); const hasDetails = detailData && Object.keys(detailData).length > 0; @@ -806,6 +837,7 @@ function RawEventRow({ event }: { event: EnrollmentEvent }) {
{event.sequence} {(recordedAt ?? new Date(event.timestamp)).toLocaleTimeString()} + {event.severity} {isBackfilled && ( ⟲