attach duration element

3clyp50 committed Jan 29, 2026 at 20:35 UTC 499b7625b7b4976b898f8720a50c6b242bfdc250
1 file changed +45 -20
webui/js/messages.js
+45 -20
@@ -239,6 +239,11 @@ function drawProcessStep({
239 const isNewStep = !step;
240 const isGroupComplete = isProcessGroupComplete(group);
241
242 + // Set start timestamp on group when first step is created
243 + if (isNewStep && !group.hasAttribute("data-start-timestamp") && log.timestamp) {
244 + group.setAttribute("data-start-timestamp", String(log.timestamp));
245 + }
246 +
247 if (isNewStep) {
248 // create the base DOM element for the step
249 step = document.createElement("div");
@@ -249,6 +254,11 @@ function drawProcessStep({
254 step.setAttribute("data-log-type", log.type);
255 step.setAttribute("data-step-id", id);
256 step.setAttribute("data-agent-number", log.agentno);
257 +
258 + // set timestamp attribute (convert to milliseconds for duration calculation)
259 + if (log.timestamp) {
260 + step.setAttribute("data-timestamp", String(Math.round(log.timestamp * 1000)));
261 + }
262
263 // apply step classes
264 if (classes) step.classList.add(...classes);
@@ -1647,7 +1657,6 @@ function createProcessGroup(id) {
1657 <span class="metric-time" title="Start time"><span class="material-symbols-outlined">schedule</span><span class="metric-value">--:--</span></span>
1658 <span class="metric-steps" title="Steps"><span class="material-symbols-outlined">footprint</span><span class="metric-value">0</span></span>
1659 <span class="metric-notifications" title="Warnings/Info/Hint" hidden><span class="material-symbols-outlined">priority_high</span><span class="metric-value">0</span></span>
1650 - <span class="metric-duration" title="Duration"><span class="material-symbols-outlined">timer</span><span class="metric-value">0s</span></span>
1660 </span>
1661 `;
1662
@@ -1918,26 +1927,42 @@ function updateProcessGroupHeader(group) {
1927 timeMetricEl.textContent = `${hours}:${minutes}`;
1928 }
1929
1921 - // Update duration metric
1922 - const durationMetricEl = metricsEl?.querySelector(
1923 - ".metric-duration .metric-value",
1930 + const firstTimestampMs = parseInt(
1931 + steps[0]?.getAttribute("data-timestamp") || "0",
1932 + 10,
1933 );
1925 - if (durationMetricEl && steps.length > 0) {
1926 - const firstTimestampMs = parseInt(
1927 - steps[0]?.getAttribute("data-timestamp") || "0",
1928 - 10,
1929 - );
1930 -
1931 - const lastStep = steps[steps.length - 1];
1932 - const lastTimestampMs = parseInt(
1933 - lastStep.getAttribute("data-timestamp") || "0",
1934 - 10,
1935 - );
1936 -
1937 - const totalDurationMs = Math.max(0, lastTimestampMs - firstTimestampMs);
1938 -
1939 - durationMetricEl.textContent = formatDuration(totalDurationMs);
1940 - }
1934 + const lastTimestampMs = parseInt(
1935 + steps[steps.length - 1]?.getAttribute("data-timestamp") || "0",
1936 + 10,
1937 + );
1938 + const durationText =
1939 + isCompleted &&
1940 + metricsEl &&
1941 + steps.length > 0 &&
1942 + firstTimestampMs > 0 &&
1943 + lastTimestampMs > 0 &&
1944 + formatDuration(Math.max(0, lastTimestampMs - firstTimestampMs));
1945 +
1946 + const durationMetricEl =
1947 + durationText &&
1948 + (() => {
1949 + const container = ensureChild(
1950 + metricsEl,
1951 + ".metric-duration",
1952 + "span",
1953 + "metric-duration",
1954 + );
1955 + container.title = "Duration";
1956 + const icon = ensureChild(
1957 + container,
1958 + ".material-symbols-outlined",
1959 + "span",
1960 + "material-symbols-outlined",
1961 + );
1962 + icon.textContent = "timer";
1963 + return ensureChild(container, ".metric-value", "span", "metric-value");
1964 + })();
1965 + durationMetricEl && (durationMetricEl.textContent = durationText);
1966
1967 if (notificationsEl) {
1968 const counts = { warning: 0, info: 0, hint: 0 };