diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 3ceeb8714f..48ce75289b 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -3073,6 +3073,8 @@ impl ContextEditor { let selection = editor.selections.newest::(cx); let mut copied_text = String::new(); let mut spanned_messages = 0; + let mut clipboard_entries: Vec = Vec::new(); + for message in context.messages(cx) { if message.offset_range.start >= selection.range().end { break; @@ -3090,8 +3092,16 @@ impl ContextEditor { } } + for image_anchor in context.image_anchors(cx) { + if let Some((render_image, _)) = context.get_image(image_anchor.image_id) { + // + } else { + log::error!("Assistant panel context had an image id of {:?} but there was no associated images entry stored for that id. This should never happen!", image_anchor.image_id); + } + } + if spanned_messages > 1 { - cx.write_to_clipboard(ClipboardItem::new_string(copied_text)); + cx.write_to_clipboard(ClipboardItem::new(clipboard_entries)); return; } } @@ -3155,11 +3165,13 @@ impl ContextEditor { let new_blocks = self .context .read(cx) - .images(cx) - .filter_map(|image| { + .image_anchors(cx) + .filter_map(|image_anchor| { const MAX_HEIGHT_IN_LINES: u32 = 8; - let anchor = buffer.anchor_in_excerpt(excerpt_id, image.anchor).unwrap(); - let image = image.render_image.clone(); + let anchor = buffer + .anchor_in_excerpt(excerpt_id, image_anchor.anchor) + .unwrap(); + let image = image_anchor.render_image.clone(); anchor.is_valid(&buffer).then(|| BlockProperties { position: anchor, height: MAX_HEIGHT_IN_LINES, diff --git a/crates/assistant/src/context.rs b/crates/assistant/src/context.rs index 095a6cea91..ad468bef40 100644 --- a/crates/assistant/src/context.rs +++ b/crates/assistant/src/context.rs @@ -1906,10 +1906,20 @@ impl Context { } } - pub fn images<'a>(&'a self, _cx: &'a AppContext) -> impl 'a + Iterator { + pub fn image_anchors<'a>( + &'a self, + _cx: &'a AppContext, + ) -> impl 'a + Iterator { self.image_anchors.iter().cloned() } + pub fn get_image( + &self, + image_id: u64, + ) -> Option<&(Arc, Shared>>)> { + self.images.get(&image_id) + } + pub fn split_message( &mut self, range: Range, diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 1c8c1681ce..4252f7ec9f 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -26,7 +26,7 @@ use crate::{ RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Scene, SharedString, Size, SvgSize, Task, TaskLabel, WindowContext, DEFAULT_WINDOW_SIZE, }; -use anyhow::Result; +use anyhow::{anyhow, Result}; use async_task::Runnable; use futures::channel::oneshot; use image::codecs::gif::GifDecoder; @@ -987,6 +987,11 @@ pub enum ClipboardEntry { } impl ClipboardItem { + /// Create a new ClipboardItem with the given entries + pub fn new(entries: Vec) -> Self { + Self { entries } + } + /// Create a new ClipboardItem::String with no associated metadata pub fn new_string(text: String) -> Self { Self { @@ -1083,6 +1088,39 @@ impl Image { ImageSource::Image(self).use_data(cx) } + /// Convert an `ImageData` object to a clipboard image in PNG format. + pub fn from_image_data(render_image: &RenderImage) -> Result { + // hardcode frame index 0; we don't currently support copying animated GIFs + if let Some(input_bytes) = render_image.as_bytes(0) { + let rgba_bytes = { + let mut buf = input_bytes.to_vec(); + + // Convert from BGRA to RGBA. + for pixel in buf.chunks_exact_mut(4) { + pixel.swap(0, 2); + } + + buf + }; + + let mut output_bytes = Vec::with_capacity(rgba_bytes.len()); + let image_buffer = RgbaIma + let cursor = Cursor::new(output_bytes); + image::DynamicImage::ImageRgba8(rgba_bytes) + .write_to(&mut cursor, image::ImageOutputFormat::Png)?; + + Ok(Self { + format: ImageFormat::Png, + bytes: cursor.into_inner(), + id: rand::random(), + }) + } else { + Err(anyhow!( + "RenderImage did not have a frame 0, which should never happen." + )) + } + } + /// Convert the clipboard image to an `ImageData` object. pub fn to_image_data(&self, cx: &AppContext) -> Result> { fn frames_for_image( diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 3d2cf0d282..9d80f63f58 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -921,6 +921,74 @@ impl Platform for MacPlatform { } } + /* + + + + + { + // write some images and text to the clip + // let image1 = ns_image(&bytes).unwrap(); + // let image2 = ns_image(&bytes).unwrap(); + let image1 = NSImage::initWithPasteboard_(nil, pasteboard); + let image2 = NSImage::initWithPasteboard_(nil, pasteboard); + // let attributed_string = text_and_images([image1, image2]); + // text_and_images([NSImage::initWithPasteboard_(, pasteboard), ns_image(&bytes).unwrap()]); + let attributed_string = { + use cocoa::appkit::NSImage; + + let image: id = msg_send![class!(NSImage), alloc]; + NSImage::initWithContentsOfFile_( + image, + NSString::alloc(nil).init_str("/Users/rtfeldman/Downloads/test.jpeg"), + ); + let size = image.size(); + + let string = NSString::alloc(nil).init_str("Test String"); + let attr_string = + NSMutableAttributedString::alloc(nil).init_attributed_string(string); + let hello_string = NSString::alloc(nil).init_str("Hello World"); + let hello_attr_string = + NSAttributedString::alloc(nil).init_attributed_string(hello_string); + attr_string.appendAttributedString_(hello_attr_string); + + let attachment = NSTextAttachment::alloc(nil); + let _: () = msg_send![attachment, setImage: image]; + let image_attr_string = msg_send![class!(NSAttributedString), attributedStringWithAttachment: attachment]; + attr_string.appendAttributedString_(image_attr_string); + + let another_string = NSString::alloc(nil).init_str("Another String"); + let another_attr_string = + NSAttributedString::alloc(nil).init_attributed_string(another_string); + attr_string.appendAttributedString_(another_attr_string); + + attr_string + }; + + pasteboard.clearContents(); + + let rtfd_data = attributed_string.RTFDFromRange_documentAttributes_( + NSRange::new(0, msg_send![attributed_string, length]), + nil, + ); + if rtfd_data != nil { + pasteboard.setData_forType(rtfd_data, NSPasteboardTypeRTFD); + } + + // let rtf_data = attributed_string.RTFFromRange_documentAttributes_( + // NSRange::new(0, attributed_string.length()), + // nil, + // ); + // if rtf_data != nil { + // pasteboard.setData_forType(rtf_data, NSPasteboardTypeRTF); + // } + + // let plain_text = attributed_string.string(); + // pasteboard.setString_forType(plain_text, NSPasteboardTypeString); + } + + */ + buf.appendAttributedString_(to_append); }