From 2cb6b1598adaa1b951bbbc854bc7aac1e821c241 Mon Sep 17 00:00:00 2001 From: mhoennig Date: Mon, 10 Aug 2026 15:45:33 +0200 Subject: [PATCH] Render live durations immediately instead of leaving them to the ticker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 10s table poll rebuilt the rows with an empty duration cell for running/pending builds (durationSeconds is null until a build finishes), which the once-per-second ticker then filled back in — a visible flicker. Rows and current-build cards now compute the elapsed time at render time. Co-Authored-By: Claude Fable 5 --- src/main/resources/static/gittally.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/resources/static/gittally.js b/src/main/resources/static/gittally.js index 57edbed..acba770 100644 --- a/src/main/resources/static/gittally.js +++ b/src/main/resources/static/gittally.js @@ -56,6 +56,24 @@ function statusCssClass(status) { return "status status-" + (KNOWN_STATUSES.has(status) ? status : "unknown"); } +/** Seconds elapsed since `startedAtIso`, or null when it is missing/invalid. */ +function elapsedSeconds(startedAtIso) { + const startedAt = new Date(startedAtIso || "").getTime(); + return Number.isNaN(startedAt) ? null : (Date.now() - startedAt) / 1000; +} + +/** + * The duration to display for a build: the recorded duration once finished, the + * live elapsed time while running or pending — so re-rendered rows never show an + * empty cell that the once-per-second ticker fills back in (visible flicker). + */ +function displayDurationSeconds(build) { + if (build.durationSeconds != null) { + return build.durationSeconds; + } + return build.status === "running" || build.status === "pending" ? elapsedSeconds(build.startedAt) : null; +} + // ---- shared infrastructure ------------------------------------------------- const FETCH_TIMEOUT_MS = 8000; @@ -216,7 +234,7 @@ function renderBuildRow(build, allowRestart) { startedCell.dataset.label = "Started"; row.appendChild(startedCell); - const durationCell = elem("td", "duration-cell", formatDuration(build.durationSeconds)); + const durationCell = elem("td", "duration-cell", formatDuration(displayDurationSeconds(build))); durationCell.dataset.label = "Duration"; row.appendChild(durationCell); @@ -309,7 +327,7 @@ function renderBuildCard(build) { ); header.appendChild(commitCode); header.appendChild(elem("span", "muted", "started " + formatTimestamp(build.startedAt))); - header.appendChild(elem("span", "duration-cell running-duration")); + header.appendChild(elem("span", "duration-cell running-duration", formatDuration(displayDurationSeconds(build)))); const cardActions = elem("span", "build-card-actions"); cardActions.appendChild( actionButton("× Cancel", "Cancel build", "cancel-button", {