inline assistant: Allow to attach images from clipboard (#32087)

Noticed while working on #31848 that we do not support pasting images as
context in the inline assistant

Release Notes:

- agent: Add support for attaching images as context from clipboard in
the inline assistant
This commit is contained in:
Bennet Bo Fenner 2025-06-04 18:43:52 +02:00 committed by GitHub
parent 2c5aa5891d
commit aefb798090
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 42 additions and 52 deletions

View file

@ -1518,31 +1518,7 @@ impl ActiveThread {
} }
fn paste(&mut self, _: &Paste, _window: &mut Window, cx: &mut Context<Self>) { fn paste(&mut self, _: &Paste, _window: &mut Window, cx: &mut Context<Self>) {
let images = cx attach_pasted_images_as_context(&self.context_store, cx);
.read_from_clipboard()
.map(|item| {
item.into_entries()
.filter_map(|entry| {
if let ClipboardEntry::Image(image) = entry {
Some(image)
} else {
None
}
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
if images.is_empty() {
return;
}
cx.stop_propagation();
self.context_store.update(cx, |store, cx| {
for image in images {
store.add_image_instance(Arc::new(image), cx);
}
});
} }
fn cancel_editing_message( fn cancel_editing_message(
@ -3653,6 +3629,38 @@ pub(crate) fn open_context(
} }
} }
pub(crate) fn attach_pasted_images_as_context(
context_store: &Entity<ContextStore>,
cx: &mut App,
) -> bool {
let images = cx
.read_from_clipboard()
.map(|item| {
item.into_entries()
.filter_map(|entry| {
if let ClipboardEntry::Image(image) = entry {
Some(image)
} else {
None
}
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
if images.is_empty() {
return false;
}
cx.stop_propagation();
context_store.update(cx, |store, cx| {
for image in images {
store.add_image_instance(Arc::new(image), cx);
}
});
true
}
fn open_editor_at_position( fn open_editor_at_position(
project_path: project::ProjectPath, project_path: project::ProjectPath,
target_position: Point, target_position: Point,

View file

@ -13,6 +13,7 @@ use assistant_context_editor::language_model_selector::ToggleModelSelector;
use client::ErrorExt; use client::ErrorExt;
use collections::VecDeque; use collections::VecDeque;
use db::kvp::Dismissable; use db::kvp::Dismissable;
use editor::actions::Paste;
use editor::display_map::EditorMargins; use editor::display_map::EditorMargins;
use editor::{ use editor::{
ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer, ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer,
@ -99,6 +100,7 @@ impl<T: 'static> Render for PromptEditor<T> {
v_flex() v_flex()
.key_context("PromptEditor") .key_context("PromptEditor")
.capture_action(cx.listener(Self::paste))
.bg(cx.theme().colors().editor_background) .bg(cx.theme().colors().editor_background)
.block_mouse_except_scroll() .block_mouse_except_scroll()
.gap_0p5() .gap_0p5()
@ -303,6 +305,10 @@ impl<T: 'static> PromptEditor<T> {
self.editor.read(cx).text(cx) self.editor.read(cx).text(cx)
} }
fn paste(&mut self, _: &Paste, _window: &mut Window, cx: &mut Context<Self>) {
crate::active_thread::attach_pasted_images_as_context(&self.context_store, cx);
}
fn toggle_rate_limit_notice( fn toggle_rate_limit_notice(
&mut self, &mut self,
_: &ClickEvent, _: &ClickEvent,

View file

@ -24,8 +24,8 @@ use fs::Fs;
use futures::future::Shared; use futures::future::Shared;
use futures::{FutureExt as _, future}; use futures::{FutureExt as _, future};
use gpui::{ use gpui::{
Animation, AnimationExt, App, ClipboardEntry, Entity, EventEmitter, Focusable, Subscription, Animation, AnimationExt, App, Entity, EventEmitter, Focusable, Subscription, Task, TextStyle,
Task, TextStyle, WeakEntity, linear_color_stop, linear_gradient, point, pulsating_between, WeakEntity, linear_color_stop, linear_gradient, point, pulsating_between,
}; };
use language::{Buffer, Language, Point}; use language::{Buffer, Language, Point};
use language_model::{ use language_model::{
@ -432,31 +432,7 @@ impl MessageEditor {
} }
fn paste(&mut self, _: &Paste, _: &mut Window, cx: &mut Context<Self>) { fn paste(&mut self, _: &Paste, _: &mut Window, cx: &mut Context<Self>) {
let images = cx crate::active_thread::attach_pasted_images_as_context(&self.context_store, cx);
.read_from_clipboard()
.map(|item| {
item.into_entries()
.filter_map(|entry| {
if let ClipboardEntry::Image(image) = entry {
Some(image)
} else {
None
}
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
if images.is_empty() {
return;
}
cx.stop_propagation();
self.context_store.update(cx, |store, cx| {
for image in images {
store.add_image_instance(Arc::new(image), cx);
}
});
} }
fn handle_review_click(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn handle_review_click(&mut self, window: &mut Window, cx: &mut Context<Self>) {