diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index c03a535..47bedb7 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -829,11 +829,13 @@ pub async fn read_tiff_preview_png(path: String) -> Result { Ok(base64::engine::general_purpose::STANDARD.encode(png_bytes.into_inner())) } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct ChartreadConfig { pub basename: String, pub cwd: String, pub port: Option, + #[serde(default)] + pub enable_i1pro2_leds: Option, } pub fn build_chartread_args(config: &ChartreadConfig) -> Vec { @@ -849,6 +851,11 @@ pub fn build_chartread_args(config: &ChartreadConfig) -> Vec { } } + if config.enable_i1pro2_leds.unwrap_or(false) { + args.push("-Y".to_string()); + args.push("l".to_string()); + } + args.push(config.basename.clone()); args } @@ -857,8 +864,12 @@ pub fn build_chartread_args(config: &ChartreadConfig) -> Vec { pub async fn run_chartread( app: AppHandle, state: State<'_, ProcessManager>, - config: ChartreadConfig, + mut config: ChartreadConfig, ) -> Result<(), String> { + if config.enable_i1pro2_leds.is_none() { + let settings = crate::settings::load_settings(app.clone()).unwrap_or_default(); + config.enable_i1pro2_leds = Some(settings.enable_i1pro2_leds); + } let binary = resolve_binary(app.clone(), "chartread".to_string()).await?; let args = build_chartread_args(&config); let id = format!("chartread_{}", config.basename); @@ -1558,6 +1569,7 @@ mod tests { basename: "my_profile".to_string(), cwd: "/home/user".to_string(), port: None, + enable_i1pro2_leds: None, }; let args = build_chartread_args(&config); assert_eq!(args, vec!["-v", "-u", "my_profile"]); @@ -1569,6 +1581,7 @@ mod tests { basename: "my_profile".to_string(), cwd: "/home/user".to_string(), port: Some("".to_string()), + enable_i1pro2_leds: None, }; let args = build_chartread_args(&config); assert_eq!(args, vec!["-v", "-u", "my_profile"]); @@ -1580,11 +1593,48 @@ mod tests { basename: "my_profile".to_string(), cwd: "/home/user".to_string(), port: Some("1".to_string()), + enable_i1pro2_leds: None, }; let args = build_chartread_args(&config); assert_eq!(args, vec!["-v", "-u", "-c", "1", "my_profile"]); } + #[test] + fn test_build_chartread_args_leds_enabled() { + let config = ChartreadConfig { + basename: "my_profile".to_string(), + cwd: "/home/user".to_string(), + port: None, + enable_i1pro2_leds: Some(true), + }; + let args = build_chartread_args(&config); + assert_eq!(args, vec!["-v", "-u", "-Y", "l", "my_profile"]); + } + + #[test] + fn test_build_chartread_args_with_port_and_leds() { + let config = ChartreadConfig { + basename: "my_profile".to_string(), + cwd: "/home/user".to_string(), + port: Some("1".to_string()), + enable_i1pro2_leds: Some(true), + }; + let args = build_chartread_args(&config); + assert_eq!(args, vec!["-v", "-u", "-c", "1", "-Y", "l", "my_profile"]); + } + + #[test] + fn test_build_chartread_args_leds_disabled() { + let config = ChartreadConfig { + basename: "my_profile".to_string(), + cwd: "/home/user".to_string(), + port: None, + enable_i1pro2_leds: Some(false), + }; + let args = build_chartread_args(&config); + assert_eq!(args, vec!["-v", "-u", "my_profile"]); + } + #[test] fn test_build_average_args() { let config = AverageConfig { diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 3716434..192a8e6 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -74,6 +74,8 @@ pub struct AppSettings { pub delta_e_warning_max: f64, #[serde(default)] pub custom_presets: Vec, + #[serde(default)] + pub enable_i1pro2_leds: bool, } impl Default for AppSettings { @@ -85,6 +87,7 @@ impl Default for AppSettings { delta_e_good_max: default_delta_e_good_max(), delta_e_warning_max: default_delta_e_warning_max(), custom_presets: Vec::new(), + enable_i1pro2_leds: false, } } } @@ -466,4 +469,28 @@ mod tests { assert_eq!(parse_log_level_filter(Some("unknown")), log::LevelFilter::Info); } } + + #[test] + fn test_default_enable_i1pro2_leds() { + let settings = AppSettings::default(); + assert_eq!(settings.enable_i1pro2_leds, false); + } + + #[test] + fn test_enable_i1pro2_leds_serialization() { + // Missing field defaults to false + let json_empty = "{}"; + let settings_empty: AppSettings = serde_json::from_str(json_empty).unwrap(); + assert_eq!(settings_empty.enable_i1pro2_leds, false); + + // Explicit true + let json_true = r#"{"enable_i1pro2_leds": true}"#; + let settings_true: AppSettings = serde_json::from_str(json_true).unwrap(); + assert_eq!(settings_true.enable_i1pro2_leds, true); + + // Explicit false + let json_false = r#"{"enable_i1pro2_leds": false}"#; + let settings_false: AppSettings = serde_json::from_str(json_false).unwrap(); + assert_eq!(settings_false.enable_i1pro2_leds, false); + } } diff --git a/src/index.html b/src/index.html index df05b8d..68bf9e2 100644 --- a/src/index.html +++ b/src/index.html @@ -883,9 +883,19 @@ -
- - +
+ +
+ + +
+
+ + Provides visual status feedback via the instrument ring LEDs during patch reading. Requires patched ArgyllCMS build. +
diff --git a/src/js/chartread.js b/src/js/chartread.js index bbcca0f..3b8158c 100644 --- a/src/js/chartread.js +++ b/src/js/chartread.js @@ -426,8 +426,11 @@ export function initChartread() { } }); + let lastStderrLine = ""; + const unlistenStderr = await listen("process:stderr", (event) => { if (event.payload.id === currentProcessId && event.payload.line) { + lastStderrLine = event.payload.line; logPre.textContent += "ERR: " + event.payload.line + "\n"; logPre.scrollTop = logPre.scrollHeight; } @@ -471,8 +474,10 @@ export function initChartread() { } } else { setState(STATE.FINISHED); - setPrompt(`❌ chartread exited with code ${event.payload.code}.`); - logPre.textContent += `\n[ERROR] chartread exited with code ${event.payload.code}.\n`; + if (logContainer) logContainer.open = true; + const errDetail = lastStderrLine ? ` (${lastStderrLine})` : ""; + setPrompt(`❌ chartread exited with code ${event.payload.code}.${errDetail}`); + logPre.textContent += `\n[ERROR] chartread exited with code ${event.payload.code}.${errDetail}\n`; } setMeasurementBusy(false); diff --git a/src/js/settings.js b/src/js/settings.js index a54ab94..5dbe5de 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -21,6 +21,7 @@ export async function initSettings() { const deltaEGoodMax = document.getElementById('deltaEGoodMax'); const deltaEWarningMax = document.getElementById('deltaEWarningMax'); const deltaEThresholdError = document.getElementById('deltaEThresholdError'); + const enableI1Pro2Leds = document.getElementById('enable_i1pro2_leds'); if (!dialog || !openBtn) return; @@ -57,6 +58,9 @@ export async function initSettings() { const settings = await invoke('load_settings'); document.getElementById('argyll_binary_dir').value = settings.argyll_binary_dir || ''; document.getElementById('default_instrument').value = settings.default_instrument || ''; + if (enableI1Pro2Leds) { + enableI1Pro2Leds.checked = Boolean(settings.enable_i1pro2_leds); + } if (logLevelSelect && settings.log_level) { logLevelSelect.value = settings.log_level; } @@ -145,6 +149,7 @@ export async function initSettings() { log_level: logLevelSelect ? logLevelSelect.value : (currentSettings.log_level || 'info'), delta_e_good_max: getInputValueAsFloat('deltaEGoodMax', 2.0), delta_e_warning_max: getInputValueAsFloat('deltaEWarningMax', 5.0), + enable_i1pro2_leds: enableI1Pro2Leds ? enableI1Pro2Leds.checked : false, }; await invoke('save_settings', { settings }); logger.info(`Settings saved. Log level set to: ${settings.log_level}`, 'Settings');