From fe199b1c0c1989a0ea619e913a990ab808f8ea4c Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Tue, 25 Aug 2026 17:45:52 +0100 Subject: [PATCH 1/5] feat(backend): add ProfilingPreset models and preset commands (fixes #88) --- src-tauri/src/settings.rs | 167 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 974d911..c444aec 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -2,10 +2,99 @@ use serde::{Deserialize, Serialize}; use std::fs; use tauri::{AppHandle, Manager}; +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +pub struct ProfilingPreset { + pub id: String, + pub name: String, + pub description: Option, + pub colour_space: String, + pub patch_count: u32, + pub white_patches: Option, + pub black_patches: Option, + pub instrument: String, + pub page_size: String, + pub bit_depth: u8, + pub dpi: u32, + pub colprof_algorithm: String, + pub colprof_quality: String, + pub colprof_intent: Option, +} + #[derive(Debug, Deserialize, Serialize, Default, Clone)] pub struct AppSettings { pub argyll_binary_dir: Option, pub default_instrument: Option, + #[serde(default)] + pub custom_presets: Vec, +} + +pub fn get_default_presets() -> Vec { + vec![ + ProfilingPreset { + id: "preset-std-rgb".to_string(), + name: "Standard RGB Photo (800 patches)".to_string(), + description: Some("Recommended for standard fine-art and photographic RGB printing on inkjet printers.".to_string()), + colour_space: "rgb".to_string(), + patch_count: 800, + white_patches: Some(4), + black_patches: None, + instrument: "i1".to_string(), + page_size: "A4".to_string(), + bit_depth: 8, + dpi: 300, + colprof_algorithm: "l".to_string(), + colprof_quality: "m".to_string(), + colprof_intent: None, + }, + ProfilingPreset { + id: "preset-hq-cmyk".to_string(), + name: "High-Gamut CMYK Proofing (1500 patches)".to_string(), + description: Some("High-precision CMYK press and RIP proofing profiling with deep shadow neutral boost.".to_string()), + colour_space: "cmyk".to_string(), + patch_count: 1500, + white_patches: None, + black_patches: Some(8), + instrument: "i1".to_string(), + page_size: "A3".to_string(), + bit_depth: 16, + dpi: 300, + colprof_algorithm: "l".to_string(), + colprof_quality: "h".to_string(), + colprof_intent: None, + }, + ProfilingPreset { + id: "preset-draft-rgb".to_string(), + name: "Fast RGB Draft (400 patches)".to_string(), + description: Some("Quick turn-around draft profiling for testing new media.".to_string()), + colour_space: "rgb".to_string(), + patch_count: 400, + white_patches: None, + black_patches: None, + instrument: "i1".to_string(), + page_size: "A4".to_string(), + bit_depth: 8, + dpi: 150, + colprof_algorithm: "l".to_string(), + colprof_quality: "l".to_string(), + colprof_intent: None, + }, + ProfilingPreset { + id: "preset-ultra-rgb".to_string(), + name: "Ultra Precision RGB (2500 patches)".to_string(), + description: Some("Maximum precision lookup table profile for exhibition and master printmaking.".to_string()), + colour_space: "rgb".to_string(), + patch_count: 2500, + white_patches: Some(6), + black_patches: Some(6), + instrument: "i1".to_string(), + page_size: "A3".to_string(), + bit_depth: 16, + dpi: 300, + colprof_algorithm: "l".to_string(), + colprof_quality: "u".to_string(), + colprof_intent: None, + }, + ] } #[tauri::command] @@ -25,3 +114,81 @@ pub fn save_settings(app: AppHandle, settings: AppSettings) -> Result<(), String let json = serde_json::to_string_pretty(&settings).unwrap(); fs::write(path.join("settings.json"), json).map_err(|e| e.to_string()) } + +#[tauri::command] +pub fn get_all_presets(app: AppHandle) -> Vec { + let mut presets = get_default_presets(); + let settings = load_settings(app).unwrap_or_default(); + presets.extend(settings.custom_presets); + presets +} + +#[tauri::command] +pub fn save_preset(app: AppHandle, preset: ProfilingPreset) -> Result, String> { + let mut settings = load_settings(app.clone()).unwrap_or_default(); + if let Some(pos) = settings.custom_presets.iter().position(|p| p.id == preset.id) { + settings.custom_presets[pos] = preset; + } else { + settings.custom_presets.push(preset); + } + save_settings(app.clone(), settings)?; + Ok(get_all_presets(app)) +} + +#[tauri::command] +pub fn delete_preset(app: AppHandle, id: String) -> Result, String> { + let mut settings = load_settings(app.clone()).unwrap_or_default(); + settings.custom_presets.retain(|p| p.id != id); + save_settings(app.clone(), settings)?; + Ok(get_all_presets(app)) +} + +#[tauri::command] +pub fn export_preset_json(preset: ProfilingPreset) -> Result { + serde_json::to_string_pretty(&preset).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn import_preset_json(json: String) -> Result { + serde_json::from_str::(&json).map_err(|e| format!("Invalid preset format: {}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_default_presets() { + let defaults = get_default_presets(); + assert_eq!(defaults.len(), 4); + assert_eq!(defaults[0].id, "preset-std-rgb"); + assert_eq!(defaults[0].colour_space, "rgb"); + assert_eq!(defaults[0].patch_count, 800); + assert_eq!(defaults[1].colour_space, "cmyk"); + assert_eq!(defaults[1].patch_count, 1500); + } + + #[test] + fn test_preset_json_export_and_import() { + let preset = ProfilingPreset { + id: "custom-test".to_string(), + name: "Custom Test Preset".to_string(), + description: Some("Test description".to_string()), + colour_space: "rgb".to_string(), + patch_count: 1200, + white_patches: Some(2), + black_patches: Some(4), + instrument: "i1".to_string(), + page_size: "A4".to_string(), + bit_depth: 16, + dpi: 300, + colprof_algorithm: "l".to_string(), + colprof_quality: "h".to_string(), + colprof_intent: None, + }; + + let json = export_preset_json(preset.clone()).expect("Export failed"); + let imported = import_preset_json(json).expect("Import failed"); + assert_eq!(preset, imported); + } +} -- 2.39.5 From 30c0734dbb2a13241fe9594d56b6ca62f624c7f0 Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Tue, 25 Aug 2026 17:45:59 +0100 Subject: [PATCH 2/5] feat(backend): register preset commands in lib.rs invoke_handler (fixes #88) --- src-tauri/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f6e5ef4..8eb4dee 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -35,6 +35,11 @@ pub fn run() { commands::print_target_native, settings::load_settings, settings::save_settings, + settings::get_all_presets, + settings::save_preset, + settings::delete_preset, + settings::export_preset_json, + settings::import_preset_json, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); -- 2.39.5 From 19c832cc24f63a84a019da98ec55aa9b9f8c9abb Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Tue, 25 Aug 2026 17:46:28 +0100 Subject: [PATCH 3/5] feat(ui): add Profiling Preset selector and management dialogs (fixes #88) --- src/index.html | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/index.html b/src/index.html index 10eb2b9..a07890e 100644 --- a/src/index.html +++ b/src/index.html @@ -32,6 +32,21 @@ + + +
+
+ +
+ + +
+
+ +
+