diff --git a/server/templates/history.html b/server/templates/history.html index 3cd917c..df81e9d 100644 --- a/server/templates/history.html +++ b/server/templates/history.html @@ -71,8 +71,11 @@ - +
+ + +
diff --git a/static/js/history.js b/static/js/history.js index 431a47a..ac88296 100644 --- a/static/js/history.js +++ b/static/js/history.js @@ -35,7 +35,8 @@ // ── Core state ────────────────────────────────────────────────────── let st = { - route: null, marker: null, points: [], timer: null, ctrl: null, activeRow: null + route: null, marker: null, points: [], timer: null, ctrl: null, activeRow: null, + maxIndex: null }; let _cache = {}; let _geoTimer = null, _geoCtrl = null, _lastKey = null; @@ -59,7 +60,9 @@ stopTm(); if (st.route) { map.removeLayer(st.route); st.route = null; } if (st.marker) { map.removeLayer(st.marker); st.marker = null; } - st.points = []; _cache = {}; _lastKey = null; + st.points = []; _cache = {}; _lastKey = null; st.maxIndex = null; + var maxTick = document.getElementById("trip-max-tick"); + if (maxTick) maxTick.hidden = true; if (_geoCtrl) { _geoCtrl.abort(); _geoCtrl = null; } clearTimeout(_geoTimer); timeline.hidden = true; summary.textContent = "Select a trip"; @@ -149,6 +152,27 @@ showPt(0); } + // ── Max-speed marker helpers ──────────────────────────────────────── + function jumpToMax() { + if (st.maxIndex == null) return; + stopTm(); + showPt(st.maxIndex); + } + function placeMaxTick() { + var tick = document.getElementById("trip-max-tick"); + var scrubber = document.getElementById("trip-scrubber"); + if (!tick || !scrubber) return; + if (st.maxIndex == null || !st.points || st.points.length < 2) { + tick.hidden = true; + return; + } + var ratio = st.maxIndex / (st.points.length - 1); + var thumb = 16; // approximate range thumb width + var usable = Math.max(1, scrubber.clientWidth - thumb); + tick.style.left = (thumb / 2 + ratio * usable) + "px"; + tick.hidden = false; + } + // ── Trip loader ───────────────────────────────────────────────────── var _deb; function loadTrip(id) { @@ -172,10 +196,14 @@ L.marker(coords[0]).bindPopup("Start").addTo(st.route); L.marker(coords[coords.length - 1]).bindPopup("End").addTo(st.route); } - // Pin the fastest point on the route. - var maxPt = null; + // Pin the fastest point on the route, remembering its index so + // clicking the marker scrubs the timeline to that point. + var maxPt = null; st.maxIndex = null; for (var i = 0; i < d.points.length; i++) { - if (!maxPt || d.points[i].speed > maxPt.speed) maxPt = d.points[i]; + if (!maxPt || d.points[i].speed > maxPt.speed) { + maxPt = d.points[i]; + st.maxIndex = i; + } } if (maxPt && maxPt.speed > 0) { L.circleMarker([maxPt.latitude, maxPt.longitude], { @@ -188,6 +216,7 @@ .bindTooltip("Max speed: " + fS(maxPt.speed), { permanent: true, direction: "top", offset: [0, -8], }) + .on("click", jumpToMax) .addTo(st.route); } st.route.addTo(map); @@ -202,12 +231,16 @@ }; summary.textContent = fK(d.trip.distance_km) + " \u00b7 " + fD(d.trip.duration_seconds) + " \u00b7 avg " + fS(d.trip.average_speed_kmh) + " \u00b7 max " + fS(d.trip.max_speed_kmh) + " \u00b7 " + d.points.length + " pts"; initTL(d.points); + placeMaxTick(); } catch (e) { if (e.name !== "AbortError") showErr(e.message); } finally { if (!sig.aborted) setLoading(false); } } // ── Scrubber & play ───────────────────────────────────────────────── scrubber.addEventListener("input", function () { stopTm(); showPt(Number(scrubber.value)); }); + var maxTick = document.getElementById("trip-max-tick"); + if (maxTick) maxTick.addEventListener("click", jumpToMax); + window.addEventListener("resize", placeMaxTick); playButton.addEventListener("click", function () { if (st.timer) { stopTm(); return; } if (Number(scrubber.value) >= st.points.length - 1) showPt(0); diff --git a/static/yaris.css b/static/yaris.css index 161573d..45df919 100644 --- a/static/yaris.css +++ b/static/yaris.css @@ -334,6 +334,21 @@ body { padding-bottom: .375rem; } +/* Max-speed tick overlaid on the trip replay scrubber. */ +.trip-max-tick { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + width: 12px; + height: 12px; + border-radius: 50%; + background: #f6c23e; + border: 2px solid #fff; + box-shadow: 0 0 0 1px rgba(0, 0, 0, .15); + cursor: pointer; + z-index: 2; +} + /* ── Dashcam / video ─────────────────────────────────────────────────────── */ /* Desktop: truncate long filenames */ diff --git a/templates/history.html b/templates/history.html index 6be8a82..416a6a0 100644 --- a/templates/history.html +++ b/templates/history.html @@ -68,8 +68,11 @@ - +
+ + +