From ea4e948aeb9b0efc97925b836307464bdbd12c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Houl=C3=A9?= <13155277+tomhoule@users.noreply.github.com> Date: Fri, 24 Apr 2026 20:30:54 +0200 Subject: [PATCH] agent_ui: Recognize image file extensions case insensitively (#54786) Straightforward. Closes https://github.com/zed-industries/zed/issues/54308 Release Notes: - Fixed the file extension recognition logic for images in the agent UI being case sensitive. It is now case insensitive. --- crates/agent_ui/src/mention_set.rs | 44 ++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/crates/agent_ui/src/mention_set.rs b/crates/agent_ui/src/mention_set.rs index f62181c9f3d..8c98b9458bb 100644 --- a/crates/agent_ui/src/mention_set.rs +++ b/crates/agent_ui/src/mention_set.rs @@ -211,10 +211,7 @@ impl MentionSet { ); let crease = if let MentionUri::File { abs_path } = &mention_uri - && let Some(extension) = abs_path.extension() - && let Some(extension) = extension.to_str() - && Img::extensions().contains(&extension) - && !extension.contains("svg") + && is_raster_image_path(abs_path) { let Some(project_path) = project .read(cx) @@ -343,12 +340,8 @@ impl MentionSet { else { return Task::ready(Err(anyhow!("project path not found"))); }; - let extension = abs_path - .extension() - .and_then(OsStr::to_str) - .unwrap_or_default(); - if Img::extensions().contains(&extension) && !extension.contains("svg") { + if is_raster_image_path(&abs_path) { if !supports_images { return Task::ready(Err(anyhow!("This model does not support images yet"))); } @@ -723,6 +716,25 @@ mod tests { other => panic!("Expected selection mention to resolve as text, got {other:?}"), } } + + #[test] + fn test_is_raster_image_path_is_case_insensitive() { + // Regression test for #54308: drag-and-dropping a file whose extension + // is uppercase (e.g. `.PNG`) used to be treated as a non-image file. + assert!(is_raster_image_path(Path::new("/tmp/image.png"))); + assert!(is_raster_image_path(Path::new("/tmp/image.PNG"))); + assert!(is_raster_image_path(Path::new("/tmp/image.Png"))); + assert!(is_raster_image_path(Path::new("/tmp/photo.JPEG"))); + assert!(is_raster_image_path(Path::new("/tmp/animation.GIF"))); + + // SVG is handled via a different code path and must not be reported here. + assert!(!is_raster_image_path(Path::new("/tmp/icon.svg"))); + assert!(!is_raster_image_path(Path::new("/tmp/icon.SVG"))); + + // Non-image extensions and paths with no extension. + assert!(!is_raster_image_path(Path::new("/tmp/notes.txt"))); + assert!(!is_raster_image_path(Path::new("/tmp/README"))); + } } /// Inserts a list of images into the editor as context mentions. @@ -846,6 +858,20 @@ fn image_format_from_external_content(format: image::ImageFormat) -> Option bool { + let Some(extension) = path.extension().and_then(OsStr::to_str) else { + return false; + }; + if extension.eq_ignore_ascii_case("svg") { + return false; + } + Img::extensions() + .iter() + .any(|known| known.eq_ignore_ascii_case(extension)) +} + pub(crate) fn load_external_image_from_path( path: &Path, default_name: &SharedString,