diff --git a/package.json b/package.json index 9589fb4..d08c29e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "iccery", "private": true, - "version": "0.3.4", + "version": "0.3.5", "type": "module", "scripts": { "fetch-argyll": "node scripts/fetch-argyll.mjs", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9318cc3..fb32adb 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iccery" -version = "0.3.4" +version = "0.3.5" description = "Modern Printer Profiling UI frontend for ArgyllCMS" authors = ["Gordon"] edition = "2021" diff --git a/src-tauri/src/process_manager.rs b/src-tauri/src/process_manager.rs index 648fe56..e956cd0 100644 --- a/src-tauri/src/process_manager.rs +++ b/src-tauri/src/process_manager.rs @@ -43,6 +43,7 @@ impl ProcessManager { command.stdout(Stdio::piped()); command.stderr(Stdio::piped()); command.stdin(Stdio::piped()); + command.env("ARGYLL_NOT_INTERACTIVE", "1"); #[cfg(windows)] { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a64731f..64bea6e 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "ICCery", - "version": "0.3.4", + "version": "0.3.5", "identifier": "com.gronod.iccery", "build": { "frontendDist": "../src" diff --git a/src/js/chartread.js b/src/js/chartread.js index d5c82bd..069fda6 100644 --- a/src/js/chartread.js +++ b/src/js/chartread.js @@ -94,22 +94,24 @@ export function initChartread() { btnDetectInstruments.textContent = "Detecting..."; setPrompt("Querying connected spectrophotometers and colorimeters via instlist..."); - const detected = []; + let stdoutAccumulator = ""; + const regexDetected = []; try { const unlistenStdout = await listen("process:stdout", (event) => { if (event.payload.id !== "instlist" || !event.payload.line) return; - const line = event.payload.line.trim(); + const line = event.payload.line; + stdoutAccumulator += line + "\n"; - // Matches Argyll instlist output e.g. "1: 'i1Pro' on 'USB'" or "1: 'ColorMunki'" or "1 = 'i1Display'" + // Legacy regex line matching fallback const KNOWN_INST_TOKENS = /i1|ColorMunki|Spyder|spectro|Display|Huey|DTP|SpectroScan|Smile|Klein/i; - const match = line.match(/^(\d+)[\s:=]+'?([^'\n]+)'?(?:\s+on\s+'?([^'\n]+)'?)?/i); + const match = line.trim().match(/^(\d+)[\s:=]+'?([^'\n]+)'?(?:\s+on\s+'?([^'\n]+)'?)?/i); if (match) { const index = match[1]; const name = match[2].trim(); const port = match[3] ? match[3].trim() : ""; if (KNOWN_INST_TOKENS.test(name) || port.length > 0) { - detected.push({ index, name, port }); + regexDetected.push({ index, name, port }); } } }); @@ -123,16 +125,35 @@ export function initChartread() { instrumentSelect.innerHTML = ``; - if (detected.length > 0) { - detected.forEach((inst) => { + let devicesList = []; + + // Try parsing JSON output from instlist + try { + const trimmed = stdoutAccumulator.trim(); + const parsed = JSON.parse(trimmed); + if (parsed && Array.isArray(parsed.devices)) { + devicesList = parsed.devices.map(d => ({ + index: String(d.port || ""), + name: d.name || d.type || "Instrument", + type: d.type || "", + port: String(d.port || ""), + })); + } + } catch (_) { + // Fall back to regex parsed items if not JSON + devicesList = regexDetected; + } + + if (devicesList.length > 0) { + devicesList.forEach((inst) => { const opt = document.createElement("option"); - // Do not pass instlist ordinal as -c comm port; use empty value for auto-port (#111) - opt.value = ""; - opt.textContent = `${inst.name}${inst.port ? ` on ${inst.port}` : ""}`; + // Port value for -c switch. If port is 1 or auto, empty string leaves -c omitted for default port + opt.value = inst.port && inst.port !== "1" ? inst.port : ""; + opt.textContent = `${inst.type || inst.name}${inst.port ? ` (Port ${inst.port})` : ""}`; instrumentSelect.appendChild(opt); }); instrumentSelect.value = ""; - setPrompt(`Found ${detected.length} instrument(s): ${detected.map(d => d.name).join(", ")}`); + setPrompt(`Found ${devicesList.length} instrument(s): ${devicesList.map(d => d.type || d.name).join(", ")}`); } else { setPrompt("No instruments found via instlist. Ensure USB cable is plugged in."); } @@ -164,6 +185,8 @@ export function initChartread() { btnStartRead.classList.remove("hidden"); break; case STATE.CALIBRATING: + btnCalibrate.disabled = false; + btnCalibrate.textContent = "✓ Calibrate"; btnCalibrate.classList.remove("hidden"); btnCancel.classList.remove("hidden"); break; @@ -243,21 +266,26 @@ export function initChartread() { // Parse prompts for state transitions const lineLower = line.toLowerCase(); - if (lineLower.includes("calibrat") && lineLower.includes("place")) { + if ( + (lineLower.includes("place") && (lineLower.includes("reference") || lineLower.includes("white") || lineLower.includes("calibrat"))) || + lineLower.includes("hit any key to continue") || + lineLower.includes("calibration") + ) { setState(STATE.CALIBRATING); - setPrompt(line); - } else if (lineLower.includes("hit") && lineLower.includes("read") && lineLower.includes("strip")) { + setPrompt(line.trim()); + } else if ( + (lineLower.includes("hit") && lineLower.includes("read") && lineLower.includes("strip")) || + lineLower.includes("ready to read") || + (lineLower.includes("read") && lineLower.includes("strip") && lineLower.includes("key")) + ) { setState(STATE.AWAITING_STRIP); - setPrompt(line); - } else if (lineLower.includes("ready to read")) { - setState(STATE.AWAITING_STRIP); - setPrompt(line); + setPrompt(line.trim()); } else if (lineLower.includes("reading strip") || lineLower.includes("processing")) { setState(STATE.READING); - setPrompt(line); + setPrompt(line.trim()); } else if (lineLower.includes("error") || lineLower.includes("too fast") || lineLower.includes("too slow") || lineLower.includes("misread")) { setState(STATE.ERROR); - setPrompt("⚠️ " + line); + setPrompt("⚠️ " + line.trim()); } }); @@ -423,8 +451,16 @@ export function initChartread() { if (btnCalibrate) { btnCalibrate.addEventListener("click", async () => { try { + btnCalibrate.disabled = true; + btnCalibrate.textContent = "⏳ Calibrating..."; + setPrompt("Sending calibration command to instrument..."); await invoke("send_stdin", { id: currentProcessId, input: " \n" }); - } catch (e) { console.error("send_stdin error:", e); } + } catch (e) { + console.error("send_stdin error:", e); + btnCalibrate.disabled = false; + btnCalibrate.textContent = "✓ Calibrate"; + setPrompt(`Failed to send calibration signal: ${e}`); + } }); }