diff --git a/web/app.js b/web/app.js
index 0c17348..ecccef3 100644
--- a/web/app.js
+++ b/web/app.js
@@ -239,8 +239,16 @@ function updateStepPlates() {
endT = forecastFinalT;
}
if (startT !== undefined && endT !== undefined) {
- const total = Math.max(0, endT - startT);
- plate.remaining.textContent = formatDuration(Math.max(0, Math.min(total, endT - currentElapsed)));
+ const stepTotal = Math.max(0, endT - startT);
+ const holdDurS = (sudSchedule[i] && sudSchedule[i].hold)
+ ? (sudSchedule[i].hold.duration || 0) * 60 : 0;
+ const rampTotal = Math.max(0, stepTotal - holdDurS);
+ const rampVal = formatDuration(
+ Math.max(0, Math.min(rampTotal, endT - currentElapsed - holdDurS)));
+ // Active step in ramp phase gets the "ramp" prefix; hold branch
+ // overwrites remaining below with hold time instead.
+ plate.remaining.textContent =
+ (sudStepIndex === i && sudStepType === 'ramp') ? 'Ramp ' + rampVal : rampVal;
} else {
plate.remaining.textContent = '-:--:--';
}
@@ -260,6 +268,11 @@ function updateStepPlates() {
plate.actual.textContent = `Actual ${plotTempIst !== null ? plotTempIst.toFixed(1) + ' °C' : '—'}`;
plate.stirrer.textContent = `Stirrer ${stirrerSpeedIst.toFixed(0)} rpm`;
plate.energy.textContent = `Energy ${energyCurrent.toFixed(0)} Wh`;
+ // Active step: ramp prefix when ramping; bare hold time when holding
+ // (reuses the same digit area). PAUSED/WAIT_USER keep the last phase.
+ if (sudStepType === 'hold') {
+ plate.remaining.textContent = 'Hold ' + formatDuration(Math.max(0, sudHoldRemaining));
+ }
} else if (i in stepActualFrozen) {
plate.actual.textContent = `Actual ${stepActualFrozen[i].toFixed(1)} °C`;
plate.stirrer.textContent = `Stirrer ${plate.configuredSpeed.toFixed(0)} rpm (cfg)`;
@@ -276,24 +289,32 @@ function updateStepPlates() {
function updateHeaderCountdown() {
const currentElapsed = (elapsed !== null) ? elapsed : 0;
const n = sudSchedule.length;
- let stepText = '-:--:--';
+ let rampText = '-:--:--';
let totalText = '-:--:--';
let visible = false;
if (n > 0 && sudStepIndex !== null) {
visible = true;
+ const startT = forecastStepStarts[sudStepIndex];
let endT = forecastStepStarts[sudStepIndex + 1];
if (endT === undefined && sudStepIndex === n - 1 && forecastFinished) {
endT = forecastFinalT;
}
- if (endT !== undefined) {
- stepText = formatDuration(Math.max(0, endT - currentElapsed));
+ if (startT !== undefined && endT !== undefined) {
+ const stepTotal = Math.max(0, endT - startT);
+ const step = sudSchedule[sudStepIndex];
+ const holdDurS = (step && step.hold) ? (step.hold.duration || 0) * 60 : 0;
+ const rampTotal = Math.max(0, stepTotal - holdDurS);
+ rampText = formatDuration(Math.max(0, Math.min(rampTotal, endT - currentElapsed - holdDurS)));
}
if (forecastFinalT !== null && elapsed !== null) {
totalText = formatDuration(Math.max(0, forecastFinalT - currentElapsed));
}
}
- document.getElementById('hdr-step-remaining').textContent = stepText;
+ const holdText = (sudStepType === 'hold' && sudHoldRemaining > 0)
+ ? formatDuration(Math.max(0, sudHoldRemaining)) : '-:--:--';
+ document.getElementById('hdr-step-remaining').textContent = rampText;
document.getElementById('hdr-total-remaining').textContent = totalText;
+ document.getElementById('hdr-hold-remaining').textContent = holdText;
document.getElementById('header-countdown').classList.toggle('hidden', !visible);
}
diff --git a/web/index.html b/web/index.html
index ded092d..9db36e1 100644
--- a/web/index.html
+++ b/web/index.html
@@ -21,7 +21,8 @@
diff --git a/web/style.css b/web/style.css
index 2805bc0..55fc136 100644
--- a/web/style.css
+++ b/web/style.css
@@ -106,13 +106,15 @@ header#connection-bar {
width: 2.8em;
}
#hdr-step-remaining,
-#hdr-total-remaining {
+#hdr-total-remaining,
+#hdr-hold-remaining {
font-family: 'Courier New', 'Lucida Console', monospace;
font-weight: bold;
font-size: 1.35em;
color: var(--accent);
}
+
/* --- Transport buttons (circular, coloured) --- */
.transport-btn {
@@ -327,7 +329,7 @@ header#connection-bar {
font-weight: bold;
text-align: right;
font-family: 'Courier New', monospace;
- color: var(--accent);
+ color: #4ade80;
}
.step-plate .field {
color: var(--text-muted);