diff --git a/crates/agent_ui/src/mention_set.rs b/crates/agent_ui/src/mention_set.rs index c09ce09223c58c57e515d181f11738f63fffbf5c..0c7b3eb6baaa372fb790cfdbd961ccfad26e27f6 100644 --- a/crates/agent_ui/src/mention_set.rs +++ b/crates/agent_ui/src/mention_set.rs @@ -839,7 +839,11 @@ fn image_format_from_external_content(format: image::ImageFormat) -> Option Some(ImageFormat::Bmp), image::ImageFormat::Tiff => Some(ImageFormat::Tiff), image::ImageFormat::Ico => Some(ImageFormat::Ico), - _ => None, + image::ImageFormat::Pnm => Some(ImageFormat::Pnm), + _ => { + debug_panic!("An unhandled image format: {format:?}"); + None + } } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index b9a3ce8e6a25cb77c80b711796704b6c37985132..59957a9c6ade0bb9f40b29cbacc2978b1ef46ce8 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -2001,17 +2001,21 @@ impl ImageFormat { } } - /// Returns the ImageFormat for the given mime type + /// Returns the ImageFormat for the given mime type, including known aliases. pub fn from_mime_type(mime_type: &str) -> Option { + use strum::IntoEnumIterator; + Self::iter() + .find(|format| format.mime_type() == mime_type) + .or_else(|| Self::from_mime_type_alias(mime_type)) + } + + /// Non-canonical mime types that some producers use in the wild. + /// Unlike `mime_type()` which returns the single canonical form, + /// these are legacy or shortened variants we still need to recognize. + fn from_mime_type_alias(mime_type: &str) -> Option { match mime_type { - "image/png" => Some(Self::Png), - "image/jpeg" | "image/jpg" => Some(Self::Jpeg), - "image/webp" => Some(Self::Webp), - "image/gif" => Some(Self::Gif), - "image/svg+xml" => Some(Self::Svg), - "image/bmp" => Some(Self::Bmp), - "image/tiff" | "image/tif" => Some(Self::Tiff), - "image/ico" => Some(Self::Ico), + "image/jpg" => Some(Self::Jpeg), + "image/tif" => Some(Self::Tiff), _ => None, } } diff --git a/crates/gpui_linux/src/linux/x11/clipboard.rs b/crates/gpui_linux/src/linux/x11/clipboard.rs index cbefea7650b0934f6fc8c46ffbe5802835ee792a..fb6cecd904591eae8029b4640709cbc401c6bef7 100644 --- a/crates/gpui_linux/src/linux/x11/clipboard.rs +++ b/crates/gpui_linux/src/linux/x11/clipboard.rs @@ -48,6 +48,7 @@ use x11rb::{ }; use gpui::{ClipboardItem, Image, ImageFormat, hash}; +use strum::IntoEnumIterator; type Result = std::result::Result; @@ -989,14 +990,8 @@ impl Clipboard { self.inner.write(data, selection, wait) } - #[allow(unused)] - pub(crate) fn set_image( - &self, - image: Image, - selection: ClipboardKind, - wait: WaitConfig, - ) -> Result<()> { - let format = match image.format { + fn image_format_atom(&self, format: ImageFormat) -> Atom { + match format { ImageFormat::Png => self.inner.atoms.PNG__MIME, ImageFormat::Jpeg => self.inner.atoms.JPEG_MIME, ImageFormat::Webp => self.inner.atoms.WEBP_MIME, @@ -1006,7 +1001,17 @@ impl Clipboard { ImageFormat::Tiff => self.inner.atoms.TIFF_MIME, ImageFormat::Ico => self.inner.atoms.ICO__MIME, ImageFormat::Pnm => self.inner.atoms.PNM__MIME, - }; + } + } + + #[allow(unused)] + pub(crate) fn set_image( + &self, + image: Image, + selection: ClipboardKind, + wait: WaitConfig, + ) -> Result<()> { + let format = self.image_format_atom(image.format); let data = vec![ClipboardData { bytes: image.bytes, format: self.inner.atoms.PNG__MIME, @@ -1015,28 +1020,11 @@ impl Clipboard { } pub(crate) fn get_any(&self, selection: ClipboardKind) -> Result { - const IMAGE_FORMAT_COUNT: usize = 7; - let image_format_atoms: [Atom; IMAGE_FORMAT_COUNT] = [ - self.inner.atoms.PNG__MIME, - self.inner.atoms.JPEG_MIME, - self.inner.atoms.WEBP_MIME, - self.inner.atoms.GIF__MIME, - self.inner.atoms.SVG__MIME, - self.inner.atoms.BMP__MIME, - self.inner.atoms.TIFF_MIME, - ]; - let image_formats: [ImageFormat; IMAGE_FORMAT_COUNT] = [ - ImageFormat::Png, - ImageFormat::Jpeg, - ImageFormat::Webp, - ImageFormat::Gif, - ImageFormat::Svg, - ImageFormat::Bmp, - ImageFormat::Tiff, - ]; + let image_entries = ImageFormat::iter() + .map(|format| (self.image_format_atom(format), format)) + .collect::>(); - const TEXT_FORMAT_COUNT: usize = 6; - let text_format_atoms: [Atom; TEXT_FORMAT_COUNT] = [ + let text_format_atoms: &[Atom] = &[ self.inner.atoms.UTF8_STRING, self.inner.atoms.UTF8_MIME_0, self.inner.atoms.UTF8_MIME_1, @@ -1045,17 +1033,11 @@ impl Clipboard { self.inner.atoms.TEXT_MIME_UNKNOWN, ]; - let atom_none: Atom = AtomEnum::NONE.into(); - - const FORMAT_ATOM_COUNT: usize = TEXT_FORMAT_COUNT + IMAGE_FORMAT_COUNT; - - let mut format_atoms: [Atom; FORMAT_ATOM_COUNT] = [atom_none; FORMAT_ATOM_COUNT]; - // image formats first, as they are more specific, and read will return the first // format that the contents can be converted to - format_atoms[0..IMAGE_FORMAT_COUNT].copy_from_slice(&image_format_atoms); - format_atoms[IMAGE_FORMAT_COUNT..].copy_from_slice(&text_format_atoms); - debug_assert!(!format_atoms.contains(&atom_none)); + let mut format_atoms = Vec::with_capacity(image_entries.len() + text_format_atoms.len()); + format_atoms.extend(image_entries.iter().map(|(atom, _)| *atom)); + format_atoms.extend_from_slice(text_format_atoms); let result = self.inner.read(&format_atoms, selection)?; @@ -1064,7 +1046,7 @@ impl Clipboard { self.inner.atom_name(result.format) ); - for (format_atom, image_format) in image_format_atoms.into_iter().zip(image_formats) { + for (format_atom, image_format) in image_entries { if result.format == format_atom { let bytes = result.bytes; let id = hash(&bytes);