From 7fb24029f2db44634f56749b4214c5e0d8492aa6 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 19:30:26 +0000 Subject: [PATCH] Part of #260: a desktop start that fails says why, in a message box naming the log When eframe could not open the window (no usable graphics device, a broken driver), main returned the error to standard error, which a Windows release build (no console) or an app started from Finder never shows: the app just vanished. The error now goes to the log (the file #286 added) and into a native message box, in the user's language, that names the log file. The saved UI language is applied before the window starts so the message uses it. Scripts and tests (LIGHTCRAFT_NO_PREFS) get no dialog. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- apps/lightcraft/src/main.rs | 35 +++++++++++++++++++-- crates/ui-egui/locales/de-formats.json | 2 ++ crates/ui-egui/locales/de.json | 1 + crates/ui-egui/locales/ja-formats.json | 2 ++ crates/ui-egui/locales/ja.json | 1 + crates/ui-egui/locales/pt-br-formats.json | 2 ++ crates/ui-egui/locales/pt-br.json | 1 + crates/ui-egui/locales/ru-formats.json | 2 ++ crates/ui-egui/locales/ru.json | 1 + crates/ui-egui/locales/zh-hans-formats.json | 2 ++ crates/ui-egui/locales/zh-hans.json | 1 + crates/ui-egui/locales/zh-hant-formats.json | 2 ++ crates/ui-egui/locales/zh-hant.json | 1 + crates/ui-egui/src/i18n.rs | 20 ++++++++++++ docs/whats-new.md | 2 ++ 15 files changed, 72 insertions(+), 3 deletions(-) diff --git a/apps/lightcraft/src/main.rs b/apps/lightcraft/src/main.rs index 97aa338b0..e0335e3f8 100644 --- a/apps/lightcraft/src/main.rs +++ b/apps/lightcraft/src/main.rs @@ -523,11 +523,15 @@ fn main() -> eframe::Result { } // The log file: opened after the arguments, so `--version`, `--help` and a usage error leave // no file behind. Records logged until now are written to it first. + let mut log_file = None; if let Some(logger) = logger { // a --memory session writes nothing (issue #164), a log file included match log_dir().filter(|_| !in_memory) { Some(dir) => match logger.attach_dir(&dir) { - Ok(path) => log::info!("LightCraft {}, log file {}", env!("CARGO_PKG_VERSION"), path.display()), + Ok(path) => { + log::info!("LightCraft {}, log file {}", env!("CARGO_PKG_VERSION"), path.display()); + log_file = Some(path); + } // The file sink has given up by now, so this goes to standard error only. Err(e) => log::warn!("no log file: {e}"), }, @@ -535,6 +539,10 @@ fn main() -> eframe::Result { } } let (prefs, prefs_warning, keep_prefs_file) = load_prefs(in_memory); + // the saved language from the start, so a failed start is reported in it too + if let Some(ui) = &prefs { + lightcraft_ui_egui::i18n::set_language(ui.language); + } // --library, else the library last opened from Settings, else the default location let library_dir = library_dir.or_else(|| { prefs @@ -564,7 +572,7 @@ fn main() -> eframe::Result { wgpu_options: window_wgpu_options(), ..Default::default() }; - eframe::run_native( + let started = eframe::run_native( "LightCraft", options, Box::new(move |cc| { @@ -618,7 +626,28 @@ fn main() -> eframe::Result { menu, ))) }), - ) + ); + if let Err(e) = &started { + startup_failed(&e.to_string(), log_file.as_deref()); + } + started +} + +/// The window could not start (e.g. no usable graphics device): say so in the log and in a native +/// message box naming the log file, instead of exiting without a trace — the release builds have +/// no console on Windows, and an app started from Finder shows no standard error (issue #260). +fn startup_failed(error: &str, log_file: Option<&std::path::Path>) { + log::error!("LightCraft could not open its window: {error}"); + if std::env::var_os("LIGHTCRAFT_NO_PREFS").is_some() { + return; // tests and scripts: no dialog to click away + } + let (title, text) = lightcraft_ui_egui::i18n::startup_failed_message(error, log_file.map(|p| p.display().to_string()).as_deref()); + let _ = rfd::MessageDialog::new() + .set_level(rfd::MessageLevel::Error) + .set_title(title) + .set_description(text) + .set_buttons(rfd::MessageButtons::Ok) + .show(); } #[cfg(test)] diff --git a/crates/ui-egui/locales/de-formats.json b/crates/ui-egui/locales/de-formats.json index 7afca56ed..adc0343d0 100644 --- a/crates/ui-egui/locales/de-formats.json +++ b/crates/ui-egui/locales/de-formats.json @@ -67,6 +67,8 @@ "Imported {} photo{}": "{} Foto{} importiert", "Insert at the cursor (also written {})": "An Cursorposition einfügen (auch als {} geschrieben)", "Label Set: {name}": "Farbmarkierungssatz: {name}", + "LightCraft could not open its window: {e}": "LightCraft konnte sein Fenster nicht öffnen: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft konnte sein Fenster nicht öffnen: {e}\n\nEinzelheiten stehen in der Protokolldatei: {path}", "MIT OR Apache-2.0. Fonts: {} (OFL). Icons: original.": "MIT ODER Apache-2.0. Schriften: {} (OFL). Symbole: eigene Gestaltung.", "Matches: {}": "Treffer: {}", "Merging… {stage} {:.0}%": "Wird zusammengefügt… {stage} {:.0}%", diff --git a/crates/ui-egui/locales/de.json b/crates/ui-egui/locales/de.json index db33e0de6..84a033c28 100644 --- a/crates/ui-egui/locales/de.json +++ b/crates/ui-egui/locales/de.json @@ -753,6 +753,7 @@ "LightCraft Preset": "LightCraft-Vorgabe", "LightCraft Presets": "LightCraft-Vorgaben", "LightCraft Website": "LightCraft-Website", + "LightCraft could not start": "LightCraft konnte nicht starten", "LightCraft couldn't find where to keep your library.": "LightCraft konnte keinen Speicherort für deine Bibliothek finden.", "LightCraft couldn't open the library at": "LightCraft konnte die Bibliothek nicht öffnen unter", "LightCraft no longer crashes at launch on Windows PCs whose Vulkan driver is broken (issue #136, e.g. some Intel UHD": "LightCraft stürzt beim Start auf Windows-PCs mit defektem Vulkan-Treiber nicht mehr ab (Issue #136, etwa bestimmte Intel-UHD-630-Treiber).", diff --git a/crates/ui-egui/locales/ja-formats.json b/crates/ui-egui/locales/ja-formats.json index 696426dbf..81e27cb08 100644 --- a/crates/ui-egui/locales/ja-formats.json +++ b/crates/ui-egui/locales/ja-formats.json @@ -35,6 +35,8 @@ "Imported {n} preset{}": "{n}件のプリセットを読み込みました{:.0}", "Imported {n} preset{}, {f} file{} not readable": "{n}件のプリセットを読み込みました{:.0}。{f}件は読み込めません{:.0}", "Imported {} photo{}": "{}枚を読み込みました{:.0}", + "LightCraft could not open its window: {e}": "LightCraftのウィンドウを開けませんでした: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraftのウィンドウを開けませんでした: {e}\n\n詳しくはログファイルをご覧ください: {path}", "Matches: {}": "一致条件:{}", "Merging… {stage} {:.0}%": "合成中… {stage} {:.0}%", "Move {what} to Recently Deleted?": "{what}を「最近削除した写真」に移動しますか?", diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index 263cc742b..d3e39f27b 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -531,6 +531,7 @@ "Library State": "ライブラリの状態", "Library saved": "ライブラリを保存しました", "Licence:": "ライセンス:", + "LightCraft could not start": "LightCraftを起動できませんでした", "Line delta": "行数の増減", "Linear": "リニア", "Lines +/−": "行数 +/−", diff --git a/crates/ui-egui/locales/pt-br-formats.json b/crates/ui-egui/locales/pt-br-formats.json index 7e8196832..14ae2f652 100644 --- a/crates/ui-egui/locales/pt-br-formats.json +++ b/crates/ui-egui/locales/pt-br-formats.json @@ -46,6 +46,8 @@ "Imported {n} preset{}, {f} file{} not readable": "Importadas {n} predefinição{:.0}, {f} arquivo{:.0} não legível", "Imported {} photo{}": "Importadas {} foto{:.0}", "Label Set: {name}": "Conjunto de rótulos: {name}", + "LightCraft could not open its window: {e}": "O LightCraft não conseguiu abrir sua janela: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "O LightCraft não conseguiu abrir sua janela: {e}\n\nO arquivo de log tem os detalhes: {path}", "Matches: {}": "Correspondências: {}", "Merging… {stage} {:.0}%": "Mesclando… {stage} {:.0}%", "Move {what} to Recently Deleted?": "Mover {what} para Excluídos recentemente?", diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index c28c1948d..6e7045bf5 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -721,6 +721,7 @@ "LightCraft Preset": "Predefinição do LightCraft", "LightCraft Presets": "Predefinições do LightCraft", "LightCraft Website": "Site do LightCraft", + "LightCraft could not start": "Não foi possível iniciar o LightCraft", "LightCraft couldn't find where to keep your library.": "O LightCraft não conseguiu encontrar onde guardar sua biblioteca.", "LightCraft couldn't open the library at": "O LightCraft não conseguiu abrir a biblioteca em", "LightCraft on GitHub": "LightCraft no GitHub", diff --git a/crates/ui-egui/locales/ru-formats.json b/crates/ui-egui/locales/ru-formats.json index 40c87ee3c..b74a1ac88 100644 --- a/crates/ui-egui/locales/ru-formats.json +++ b/crates/ui-egui/locales/ru-formats.json @@ -71,6 +71,8 @@ "Insert at the cursor (also written {})": "Вставить в позицию курсора (также пишется как {})", "Label Set: {name}": "Набор меток: {name}", "LightCraft can't decode this raw variant yet ({}). You're editing the camera's embedded JPEG preview, which already includes the camera's picture style (e.g. Monochrome) and white balance.": "LightCraft пока не умеет декодировать этот вариант RAW ({}). Вы редактируете встроенный в файл JPEG-просмотр камеры, в котором уже применены стиль изображения камеры (например, «Монохром») и баланс белого.", + "LightCraft could not open its window: {e}": "LightCraft не удалось открыть окно: {e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft не удалось открыть окно: {e}\n\nПодробности в файле журнала: {path}", "MIT OR Apache-2.0. Fonts: {} (OFL). Icons: original.": "MIT OR Apache-2.0. Шрифты: {} (OFL). Значки: оригинальные.", "Matches: {}": "Совпадения: {}", "Merging… {stage} {:.0}%": "Слияние… {stage} {:.0}%", diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json index 913c85ad6..6cb076dbb 100644 --- a/crates/ui-egui/locales/ru.json +++ b/crates/ui-egui/locales/ru.json @@ -590,6 +590,7 @@ "LightCraft Preset": "Пресет LightCraft", "LightCraft Presets": "Пресеты LightCraft", "LightCraft Website": "Сайт LightCraft", + "LightCraft could not start": "Не удалось запустить LightCraft", "LightCraft couldn't find where to keep your library.": "LightCraft не удалось найти место для хранения вашей библиотеки.", "LightCraft couldn't open the library at": "LightCraft не удалось открыть библиотеку в", "LightCraft on GitHub": "LightCraft на GitHub", diff --git a/crates/ui-egui/locales/zh-hans-formats.json b/crates/ui-egui/locales/zh-hans-formats.json index 9a27c6b21..d342e30f2 100644 --- a/crates/ui-egui/locales/zh-hans-formats.json +++ b/crates/ui-egui/locales/zh-hans-formats.json @@ -35,6 +35,8 @@ "Imported {n} preset{}": "已导入 {n} 个预设{0:.0}", "Imported {n} preset{}, {f} file{} not readable": "已导入 {n} 个预设,{f} 个文件无法读取{0:.0}{1:.0}", "Imported {} photo{}": "已导入 {0} 张照片{1:.0}", + "LightCraft could not open its window: {e}": "LightCraft 无法打开窗口:{e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft 无法打开窗口:{e}\n\n详细信息见日志文件:{path}", "Matches: {}": "匹配项:{0}", "Merging… {stage} {:.0}%": "合成中… {stage} {0:.0}%", "Move {what} to Recently Deleted?": "将 {what} 移到最近删除?", diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 750137311..c42e2abd3 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -523,6 +523,7 @@ "Lens Blur": "镜头模糊", "Library saved": "图库已保存", "Licence:": "许可证:", + "LightCraft could not start": "LightCraft 无法启动", "Line delta": "行数差", "Linear": "线性", "Lines +/−": "行数 +/−", diff --git a/crates/ui-egui/locales/zh-hant-formats.json b/crates/ui-egui/locales/zh-hant-formats.json index b9d0b8083..674a18109 100644 --- a/crates/ui-egui/locales/zh-hant-formats.json +++ b/crates/ui-egui/locales/zh-hant-formats.json @@ -43,6 +43,8 @@ "Imported {n} preset{}, {f} file{} not readable": "已匯入 {n} 個預設集{:.0},{f} 個檔案無法讀取{:.0}", "Imported {} photo{}": "已匯入 {} 張照片{:.0}", "Label Set: {name}": "標籤集:{name}", + "LightCraft could not open its window: {e}": "LightCraft 無法開啟視窗:{e}", + "LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft 無法開啟視窗:{e}\n\n詳細資訊請見記錄檔:{path}", "Matches: {}": "符合條件:{}", "Merging… {stage} {:.0}%": "正在合併… {stage} {:.0}%", "Move {what} to Recently Deleted?": "要將{what}移至「最近刪除」嗎?", diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 278255b43..c097f4f68 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -523,6 +523,7 @@ "Lens Blur": "鏡頭模糊", "Library saved": "照片庫已儲存", "Licence:": "授權:", + "LightCraft could not start": "LightCraft 無法啟動", "Line delta": "行數差", "Linear": "線性", "Lines +/−": "行數 +/−", diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index cba285853..76999b25e 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -189,6 +189,16 @@ include!(concat!(env!("OUT_DIR"), "/tr-formats.rs")); /// A stock preset's or profile's name in the UI language; an imported or user-edited name stays /// exactly as its owner wrote it. +/// The title and text of the message box shown when the desktop app's window can't start +/// (issue #260), in the UI language: the error and, when there is one, the log file to look in. +pub fn startup_failed_message(error: &str, log_file: Option<&str>) -> (String, String) { + let text = match log_file { + Some(path) => tr_format!("LightCraft could not open its window: {e}\n\nThe log file has the details: {path}", e = error, path = path), + None => tr_format!("LightCraft could not open its window: {e}", e = error), + }; + (tr("LightCraft could not start").to_string(), text) +} + pub fn builtin_label(source: &str, builtin: bool) -> &str { if builtin { tr(source) } else { source } } @@ -404,6 +414,16 @@ mod tests { } /// Locale tags are matched leniently, so a system locale reaches a shipped language. + /// The message box for a desktop start that failed (issue #260) names the error and the log + /// file, in the UI language. + #[test] + fn startup_failure_message_names_the_error_and_the_log() { + let (title, text) = startup_failed_message("no adapter", Some("/home/a/.config/lightcraft/logs/lightcraft.log")); + assert_eq!(title, "LightCraft could not start"); + assert!(text.contains("no adapter") && text.ends_with("lightcraft.log"), "{text}"); + assert!(!startup_failed_message("no adapter", None).1.contains("log file")); + } + #[test] fn locale_tags_normalize() { assert_eq!(Locale::parse_tag("en"), Some(Locale::En)); diff --git a/docs/whats-new.md b/docs/whats-new.md index 05f8dbfb4..9569853ae 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -63,6 +63,8 @@ promise, instead of filtering whatever album or folder was open, which often showed nothing (issue #341). ### Reliability +- If the desktop app can't open its window (for example when no graphics device can be used), it now says so in a + message box that names the log file, instead of quitting without a trace (issue #260). - On macOS, single-key shortcuts that appear in the menu bar now work: E, C, H, M, ⇧P, I, K, D, ratings 0–5, labels 6–9, P / U and the rest did nothing, because macOS only passes ⌘ / ⌃ combinations and function keys to the menu bar and the app ignored those keys, assuming the menu bar would handle them. Keys outside the menus