Merge pull request #370 from storytold/claude/fix-260-startup-failure-dialog

Part of #260: a desktop start that fails says why, in a message box naming the log
This commit is contained in:
BFlat
2026-10-09 02:43:05 +07:00
committed by GitHub
15 changed files with 72 additions and 3 deletions
+32 -3
View File
@@ -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)]
+2
View File
@@ -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}%",
+1
View File
@@ -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).",
+2
View File
@@ -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}を「最近削除した写真」に移動しますか?",
+1
View File
@@ -531,6 +531,7 @@
"Library State": "ライブラリの状態",
"Library saved": "ライブラリを保存しました",
"Licence:": "ライセンス:",
"LightCraft could not start": "LightCraftを起動できませんでした",
"Line delta": "行数の増減",
"Linear": "リニア",
"Lines +/−": "行数 +/−",
@@ -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?",
+1
View File
@@ -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",
+2
View File
@@ -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}%",
+1
View File
@@ -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",
@@ -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} 移到最近删除?",
+1
View File
@@ -523,6 +523,7 @@
"Lens Blur": "镜头模糊",
"Library saved": "图库已保存",
"Licence:": "许可证:",
"LightCraft could not start": "LightCraft 无法启动",
"Line delta": "行数差",
"Linear": "线性",
"Lines +/−": "行数 +/−",
@@ -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}移至「最近刪除」嗎?",
+1
View File
@@ -523,6 +523,7 @@
"Lens Blur": "鏡頭模糊",
"Library saved": "照片庫已儲存",
"Licence:": "授權:",
"LightCraft could not start": "LightCraft 無法啟動",
"Line delta": "行數差",
"Linear": "線性",
"Lines +/−": "行數 +/−",
+20
View File
@@ -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));
+2
View File
@@ -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