From 829a841d9ba01e90050548fd3497aa3c77fcbd96 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Fri, 21 Aug 2026 19:09:03 +0200 Subject: [PATCH] feat(web): slanted inline labels for network-timeline lifecycle markers The gray lifecycle dots (Reboot / Desktop / Completed) were hard to hit and mute without hovering. Their label is now written directly at the dot, slanted up-right (flipping up-left near the right edge so it stays inside the drawing), with the band shifted down to make headroom. The whole marker group (line, dot, label) shares an enlarged invisible hit target, and a label is suppressed when its neighbor sits too close - the tooltip still carries the details. Co-Authored-By: Claude Fable 5 --- .../sessions/network-timeline/NetworkBand.tsx | 94 +++++++++++++------ 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/src/Web/autopilot-monitor-web/app/sessions/network-timeline/NetworkBand.tsx b/src/Web/autopilot-monitor-web/app/sessions/network-timeline/NetworkBand.tsx index 3927e45a..da122589 100644 --- a/src/Web/autopilot-monitor-web/app/sessions/network-timeline/NetworkBand.tsx +++ b/src/Web/autopilot-monitor-web/app/sessions/network-timeline/NetworkBand.tsx @@ -41,12 +41,15 @@ export default function NetworkBand({ model }: { model: NetworkModel }) { const { segments, colors, scale, phases, checks, lifeMarkers } = model; - const PHASE_Y = 8; + // Headroom above the phase lane for the slanted lifecycle-marker labels + // (Reboot / Desktop / Completed …) written directly at their dots. + const MARKER_LABEL_H = 32; + const PHASE_Y = 8 + MARKER_LABEL_H; const PHASE_H = 22; - const BAND_Y = 40; + const BAND_Y = PHASE_Y + 32; const BAND_H = 58; const TICK_Y = BAND_Y + BAND_H; - const H = 172; + const H = 172 + MARKER_LABEL_H; // Boundary ticks: real times at every segment boundary, staggered rows, // labels suppressed when they would collide. @@ -61,6 +64,17 @@ export default function NetworkBand({ model }: { model: NetworkModel }) { } }); + // Lifecycle-marker label layout: a marker's slanted label is suppressed when + // the previous shown label sits closer than 30 units (tooltip still works). + const markerXs = lifeMarkers.map((m) => Math.min(Math.max(scale.x(m.t), 6), W - 6)); + const markerLabelVisible: boolean[] = []; + const lastLabelX = [-100]; + for (const x of markerXs) { + const show = x - lastLabelX[0] >= 30; + if (show) lastLabelX[0] = x; + markerLabelVisible.push(show); + } + // Legend entries in first-seen order; the strongest hotspot hint wins. const legend: { identity: string; label: string; color: string; kind: SegmentKind; hotspot?: HotspotHint }[] = []; for (const seg of segments) { @@ -249,38 +263,56 @@ export default function NetworkBand({ model }: { model: NetworkModel }) { ); })} - {/* Lifecycle markers (reboot / desktop / terminal) */} + {/* Lifecycle markers (reboot / desktop / terminal) — labeled directly + at the dot, slanted up-right; near the right edge the label flips + to up-left so it never leaves the drawing. A label is suppressed + (tooltip still works) when its neighbor's label sits too close. */} {lifeMarkers.map((m, i) => { - const x = Math.min(Math.max(scale.x(m.t), 6), W - 6); - const tip = ( -
-
{m.label}
-
{fmtTime(m.t)}
-
- ); - return ( - - - W - 90; + const lx = nearRightEdge ? x - 7 : x + 7; + const ly = PHASE_Y - 10; + const tip = ( +
+
{m.label}
+
{fmtTime(m.t)}
+
+ ); + return ( + showTooltip(e, tip)} onClick={(e) => showTooltip(e, tip)} onMouseLeave={hideTooltip} - /> - - ); + > + + + {/* invisible enlarged hit target — the dot alone is hard to hover */} + + {showLabel && ( + + {m.label} + + )} +
+ ); })} {/* Connectivity check markers */}