diff --git a/src/io/file_association.rs b/src/io/file_association.rs index 722da5202..c9e85b45d 100644 --- a/src/io/file_association.rs +++ b/src/io/file_association.rs @@ -367,6 +367,9 @@ mod linux_impl { std::fs::create_dir_all(&apps).map_err(|e| e.to_string())?; std::fs::write(&desktop_path, &contents).map_err(|e| e.to_string())?; + + install_icon()?; + // Best-effort: refresh the MIME→app cache. Absent on minimal systems. let _ = std::process::Command::new("update-desktop-database") .arg(&apps) @@ -374,6 +377,34 @@ mod linux_impl { Ok(()) } + /// Write the app SVG icon into the user's hicolor icon theme so that the + /// named `Icon={APP_ID}` entry in the .desktop file resolves correctly in + /// file managers and the "Open with" picker. Skipped when the icon is + /// already up-to-date; best-effort icon-cache refresh afterwards. + fn install_icon() -> Result<(), String> { + static LOGO_SVG: &[u8] = include_bytes!("../../assets/logo.svg"); + + let icon_dir = data_home()?.join("icons/hicolor/scalable/apps"); + let icon_path = icon_dir.join(format!("{APP_ID}.svg")); + + // Skip write when the on-disk file is already identical. + if std::fs::read(&icon_path).ok().as_deref() == Some(LOGO_SVG) { + return Ok(()); + } + + std::fs::create_dir_all(&icon_dir).map_err(|e| e.to_string())?; + std::fs::write(&icon_path, LOGO_SVG).map_err(|e| e.to_string())?; + + // Refresh the icon cache so desktop environments pick up the new file. + let hicolor = data_home() + .map(|d| d.join("icons/hicolor")) + .unwrap_or_default(); + let _ = std::process::Command::new("gtk-update-icon-cache") + .args(["--force", "--quiet", &hicolor.to_string_lossy()]) + .status(); + Ok(()) + } + fn data_home() -> Result { std::env::var_os("XDG_DATA_HOME") .map(PathBuf::from)