feat(chartread): Add Accept/Override button and improve prompt handling for warnings and multi-sheet reads (resolves #137) #138

Merged
gronod merged 1 commits from feat/137-chartread-accept-override-prompts into development 2026-08-29 17:22:56 +01:00
3 changed files with 95 additions and 20 deletions
+1
View File
@@ -330,6 +330,7 @@
<div class="chartread-actions"> <div class="chartread-actions">
<button class="primary" id="btnStartRead">Start Measurement</button> <button class="primary" id="btnStartRead">Start Measurement</button>
<button class="primary hidden" id="btnCalibrate">✓ Calibrate</button> <button class="primary hidden" id="btnCalibrate">✓ Calibrate</button>
<button class="primary hidden" id="btnAccept">✓ Accept Strip</button>
<button class="secondary hidden" id="btnRetry">↻ Retry Strip</button> <button class="secondary hidden" id="btnRetry">↻ Retry Strip</button>
<button class="secondary hidden" id="btnSkip">⏭ Skip Strip</button> <button class="secondary hidden" id="btnSkip">⏭ Skip Strip</button>
<button class="danger hidden" id="btnCancel">✕ Cancel</button> <button class="danger hidden" id="btnCancel">✕ Cancel</button>
+93 -20
View File
@@ -23,6 +23,8 @@ const STATE = {
CALIBRATING: "CALIBRATING", CALIBRATING: "CALIBRATING",
AWAITING_STRIP: "AWAITING_STRIP", AWAITING_STRIP: "AWAITING_STRIP",
READING: "READING", READING: "READING",
WARNING: "WARNING",
PROMPT_CONTINUE: "PROMPT_CONTINUE",
ERROR: "ERROR", ERROR: "ERROR",
FINISHED: "FINISHED", FINISHED: "FINISHED",
}; };
@@ -36,6 +38,7 @@ const recordedPasses = [];
export function initChartread() { export function initChartread() {
const btnStartRead = document.getElementById("btnStartRead"); const btnStartRead = document.getElementById("btnStartRead");
const btnCalibrate = document.getElementById("btnCalibrate"); const btnCalibrate = document.getElementById("btnCalibrate");
const btnAccept = document.getElementById("btnAccept");
const btnRetry = document.getElementById("btnRetry"); const btnRetry = document.getElementById("btnRetry");
const btnSkip = document.getElementById("btnSkip"); const btnSkip = document.getElementById("btnSkip");
const btnCancel = document.getElementById("btnCancel"); const btnCancel = document.getElementById("btnCancel");
@@ -174,39 +177,71 @@ export function initChartread() {
if (stateLabel) stateLabel.textContent = newState; if (stateLabel) stateLabel.textContent = newState;
// Show/hide buttons based on state // Show/hide buttons based on state
btnCalibrate.classList.add("hidden"); if (btnCalibrate) btnCalibrate.classList.add("hidden");
btnRetry.classList.add("hidden"); if (btnAccept) btnAccept.classList.add("hidden");
btnSkip.classList.add("hidden"); if (btnRetry) btnRetry.classList.add("hidden");
btnCancel.classList.add("hidden"); if (btnSkip) btnSkip.classList.add("hidden");
btnStartRead.classList.add("hidden"); if (btnCancel) btnCancel.classList.add("hidden");
if (btnStartRead) btnStartRead.classList.add("hidden");
switch (newState) { switch (newState) {
case STATE.IDLE: case STATE.IDLE:
btnStartRead.classList.remove("hidden"); if (btnStartRead) btnStartRead.classList.remove("hidden");
break; break;
case STATE.CALIBRATING: case STATE.CALIBRATING:
btnCalibrate.disabled = false; if (btnCalibrate) {
btnCalibrate.textContent = "✓ Calibrate"; btnCalibrate.disabled = false;
btnCalibrate.classList.remove("hidden"); btnCalibrate.textContent = "✓ Calibrate";
btnCancel.classList.remove("hidden"); btnCalibrate.classList.remove("hidden");
}
if (btnCancel) btnCancel.classList.remove("hidden");
break; break;
case STATE.AWAITING_STRIP: case STATE.AWAITING_STRIP:
btnRetry.classList.remove("hidden"); if (btnRetry) {
btnSkip.classList.remove("hidden"); btnRetry.disabled = false;
btnCancel.classList.remove("hidden"); btnRetry.textContent = "↻ Retry Strip";
btnRetry.classList.remove("hidden");
}
if (btnSkip) btnSkip.classList.remove("hidden");
if (btnCancel) btnCancel.classList.remove("hidden");
break; break;
case STATE.READING: case STATE.READING:
btnCancel.classList.remove("hidden"); if (btnCancel) btnCancel.classList.remove("hidden");
break;
case STATE.WARNING:
if (btnAccept) {
btnAccept.disabled = false;
btnAccept.textContent = "✓ Accept Strip";
btnAccept.classList.remove("hidden");
}
if (btnRetry) {
btnRetry.disabled = false;
btnRetry.textContent = "↻ Retry Strip";
btnRetry.classList.remove("hidden");
}
if (btnCancel) btnCancel.classList.remove("hidden");
break;
case STATE.PROMPT_CONTINUE:
if (btnAccept) {
btnAccept.disabled = false;
btnAccept.textContent = "✓ Continue";
btnAccept.classList.remove("hidden");
}
if (btnCancel) btnCancel.classList.remove("hidden");
break; break;
case STATE.ERROR: case STATE.ERROR:
btnRetry.classList.remove("hidden"); if (btnRetry) {
btnSkip.classList.remove("hidden"); btnRetry.disabled = false;
btnCancel.classList.remove("hidden"); btnRetry.textContent = "↻ Retry Strip";
btnRetry.classList.remove("hidden");
}
if (btnSkip) btnSkip.classList.remove("hidden");
if (btnCancel) btnCancel.classList.remove("hidden");
break; break;
case STATE.FINISHED: case STATE.FINISHED:
// After a recorded pass, further sheets go through "Measure Another Sheet" // After a recorded pass, further sheets go through "Measure Another Sheet"
if (recordedPasses.length === 0) { if (recordedPasses.length === 0) {
btnStartRead.classList.remove("hidden"); if (btnStartRead) btnStartRead.classList.remove("hidden");
} }
break; break;
} }
@@ -267,7 +302,29 @@ export function initChartread() {
const lineLower = line.toLowerCase(); const lineLower = line.toLowerCase();
if ( if (
(lineLower.includes("place") && (lineLower.includes("reference") || lineLower.includes("white") || lineLower.includes("calibrat"))) || lineLower.includes("(warning)") ||
lineLower.includes("use it anyway") ||
lineLower.includes("seem to have read strip pass") ||
lineLower.includes("unexpected response") ||
lineLower.includes("hit return to use it anyway")
) {
const previousPrompt = promptText ? promptText.textContent.trim() : "";
const isContinuationPrompt = lineLower.includes("hit return to use it anyway") || lineLower.includes("use it anyway");
setState(STATE.WARNING);
if (currentState === STATE.WARNING && isContinuationPrompt && previousPrompt && !previousPrompt.includes(line.trim())) {
setPrompt(`${previousPrompt}\n${line.trim()}`);
} else {
setPrompt(line.trim());
}
} else if (
lineLower.includes("place sheet") ||
lineLower.includes("remove previous sheet") ||
(lineLower.includes("hit return to continue") && !lineLower.includes("use it anyway"))
) {
setState(STATE.PROMPT_CONTINUE);
setPrompt(line.trim());
} else if (
(lineLower.includes("place") && (lineLower.includes("reference") || lineLower.includes("white") || lineLower.includes("calibrat") || lineLower.includes("standard"))) ||
lineLower.includes("hit any key to continue") || lineLower.includes("hit any key to continue") ||
lineLower.includes("calibration") lineLower.includes("calibration")
) { ) {
@@ -283,7 +340,7 @@ export function initChartread() {
} else if (lineLower.includes("reading strip") || lineLower.includes("processing")) { } else if (lineLower.includes("reading strip") || lineLower.includes("processing")) {
setState(STATE.READING); setState(STATE.READING);
setPrompt(line.trim()); setPrompt(line.trim());
} else if (lineLower.includes("error") || lineLower.includes("too fast") || lineLower.includes("too slow") || lineLower.includes("misread")) { } else if (lineLower.includes("error") || lineLower.includes("too fast") || lineLower.includes("too slow") || lineLower.includes("misread") || lineLower.includes("failed to read")) {
setState(STATE.ERROR); setState(STATE.ERROR);
setPrompt("⚠️ " + line.trim()); setPrompt("⚠️ " + line.trim());
} }
@@ -464,6 +521,22 @@ export function initChartread() {
}); });
} }
// Accept / Continue button — sends newline ("\n")
if (btnAccept) {
btnAccept.addEventListener("click", async () => {
try {
btnAccept.disabled = true;
await invoke("send_stdin", { id: currentProcessId, input: "\n" });
setState(STATE.READING);
setPrompt("Accepted. Processing...");
} catch (e) {
console.error("send_stdin error:", e);
btnAccept.disabled = false;
setPrompt(`Failed to send accept signal: ${e}`);
}
});
}
// Retry button — sends space + newline (same as confirm) // Retry button — sends space + newline (same as confirm)
if (btnRetry) { if (btnRetry) {
btnRetry.addEventListener("click", async () => { btnRetry.addEventListener("click", async () => {
+1
View File
@@ -455,6 +455,7 @@ button:disabled {
color: #fff; color: #fff;
line-height: 1.4; line-height: 1.4;
min-height: 2.8em; min-height: 2.8em;
white-space: pre-line;
} }
.chartread-actions { .chartread-actions {