agent: Show actual file name and icon in context pill (#31813)

Previously in the agent context pill if we added images it showed
generic Image tag on the image context pill. This PR make sure if we
have a path available for a image context show the filename which is in
line with other context pills.


Before | After
--- | ---
![Screenshot 2025-05-31 at 3 14 07
PM](https://github.com/user-attachments/assets/b342f046-2c1c-4c18-bb26-2926933d5d34)
| ![Screenshot 2025-05-31 at 3 14 07
PM](https://github.com/user-attachments/assets/90ad4062-cdc6-4274-b9cd-834b76e8e11b)





Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
This commit is contained in:
Umesh Yadav 2025-06-02 16:05:22 +05:30 committed by GitHub
parent ae219e9e99
commit 9c715b470e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 81 additions and 50 deletions

View file

@ -734,6 +734,7 @@ impl Display for RulesContext {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ImageContext { pub struct ImageContext {
pub project_path: Option<ProjectPath>, pub project_path: Option<ProjectPath>,
pub full_path: Option<Arc<Path>>,
pub original_image: Arc<gpui::Image>, pub original_image: Arc<gpui::Image>,
// TODO: handle this elsewhere and remove `ignore-interior-mutability` opt-out in clippy.toml // TODO: handle this elsewhere and remove `ignore-interior-mutability` opt-out in clippy.toml
// needed due to a false positive of `clippy::mutable_key_type`. // needed due to a false positive of `clippy::mutable_key_type`.

View file

@ -7,7 +7,7 @@ use assistant_context_editor::AssistantContext;
use collections::{HashSet, IndexSet}; use collections::{HashSet, IndexSet};
use futures::{self, FutureExt}; use futures::{self, FutureExt};
use gpui::{App, Context, Entity, EventEmitter, Image, SharedString, Task, WeakEntity}; use gpui::{App, Context, Entity, EventEmitter, Image, SharedString, Task, WeakEntity};
use language::Buffer; use language::{Buffer, File as _};
use language_model::LanguageModelImage; use language_model::LanguageModelImage;
use project::image_store::is_image_file; use project::image_store::is_image_file;
use project::{Project, ProjectItem, ProjectPath, Symbol}; use project::{Project, ProjectItem, ProjectPath, Symbol};
@ -304,11 +304,13 @@ impl ContextStore {
project.open_image(project_path.clone(), cx) project.open_image(project_path.clone(), cx)
})?; })?;
let image_item = open_image_task.await?; let image_item = open_image_task.await?;
let image = image_item.read_with(cx, |image_item, _| image_item.image.clone())?;
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
let item = image_item.read(cx);
this.insert_image( this.insert_image(
Some(image_item.read(cx).project_path(cx)), Some(item.project_path(cx)),
image, Some(item.file.full_path(cx).into()),
item.image.clone(),
remove_if_exists, remove_if_exists,
cx, cx,
) )
@ -317,12 +319,13 @@ impl ContextStore {
} }
pub fn add_image_instance(&mut self, image: Arc<Image>, cx: &mut Context<ContextStore>) { pub fn add_image_instance(&mut self, image: Arc<Image>, cx: &mut Context<ContextStore>) {
self.insert_image(None, image, false, cx); self.insert_image(None, None, image, false, cx);
} }
fn insert_image( fn insert_image(
&mut self, &mut self,
project_path: Option<ProjectPath>, project_path: Option<ProjectPath>,
full_path: Option<Arc<Path>>,
image: Arc<Image>, image: Arc<Image>,
remove_if_exists: bool, remove_if_exists: bool,
cx: &mut Context<ContextStore>, cx: &mut Context<ContextStore>,
@ -330,6 +333,7 @@ impl ContextStore {
let image_task = LanguageModelImage::from_image(image.clone(), cx).shared(); let image_task = LanguageModelImage::from_image(image.clone(), cx).shared();
let context = AgentContextHandle::Image(ImageContext { let context = AgentContextHandle::Image(ImageContext {
project_path, project_path,
full_path,
original_image: image, original_image: image,
image_task, image_task,
context_id: self.next_context_id.post_inc(), context_id: self.next_context_id.post_inc(),

View file

@ -304,7 +304,7 @@ impl AddedContext {
AgentContextHandle::Thread(handle) => Some(Self::pending_thread(handle, cx)), AgentContextHandle::Thread(handle) => Some(Self::pending_thread(handle, cx)),
AgentContextHandle::TextThread(handle) => Some(Self::pending_text_thread(handle, cx)), AgentContextHandle::TextThread(handle) => Some(Self::pending_text_thread(handle, cx)),
AgentContextHandle::Rules(handle) => Self::pending_rules(handle, prompt_store, cx), AgentContextHandle::Rules(handle) => Self::pending_rules(handle, prompt_store, cx),
AgentContextHandle::Image(handle) => Some(Self::image(handle)), AgentContextHandle::Image(handle) => Some(Self::image(handle, cx)),
} }
} }
@ -318,7 +318,7 @@ impl AddedContext {
AgentContext::Thread(context) => Self::attached_thread(context), AgentContext::Thread(context) => Self::attached_thread(context),
AgentContext::TextThread(context) => Self::attached_text_thread(context), AgentContext::TextThread(context) => Self::attached_text_thread(context),
AgentContext::Rules(context) => Self::attached_rules(context), AgentContext::Rules(context) => Self::attached_rules(context),
AgentContext::Image(context) => Self::image(context.clone()), AgentContext::Image(context) => Self::image(context.clone(), cx),
} }
} }
@ -333,14 +333,8 @@ impl AddedContext {
fn file(handle: FileContextHandle, full_path: &Path, cx: &App) -> AddedContext { fn file(handle: FileContextHandle, full_path: &Path, cx: &App) -> AddedContext {
let full_path_string: SharedString = full_path.to_string_lossy().into_owned().into(); let full_path_string: SharedString = full_path.to_string_lossy().into_owned().into();
let name = full_path let (name, parent) =
.file_name() extract_file_name_and_directory_from_full_path(full_path, &full_path_string);
.map(|n| n.to_string_lossy().into_owned().into())
.unwrap_or_else(|| full_path_string.clone());
let parent = full_path
.parent()
.and_then(|p| p.file_name())
.map(|n| n.to_string_lossy().into_owned().into());
AddedContext { AddedContext {
kind: ContextKind::File, kind: ContextKind::File,
name, name,
@ -370,14 +364,8 @@ impl AddedContext {
fn directory(handle: DirectoryContextHandle, full_path: &Path) -> AddedContext { fn directory(handle: DirectoryContextHandle, full_path: &Path) -> AddedContext {
let full_path_string: SharedString = full_path.to_string_lossy().into_owned().into(); let full_path_string: SharedString = full_path.to_string_lossy().into_owned().into();
let name = full_path let (name, parent) =
.file_name() extract_file_name_and_directory_from_full_path(full_path, &full_path_string);
.map(|n| n.to_string_lossy().into_owned().into())
.unwrap_or_else(|| full_path_string.clone());
let parent = full_path
.parent()
.and_then(|p| p.file_name())
.map(|n| n.to_string_lossy().into_owned().into());
AddedContext { AddedContext {
kind: ContextKind::Directory, kind: ContextKind::Directory,
name, name,
@ -605,13 +593,23 @@ impl AddedContext {
} }
} }
fn image(context: ImageContext) -> AddedContext { fn image(context: ImageContext, cx: &App) -> AddedContext {
let (name, parent, icon_path) = if let Some(full_path) = context.full_path.as_ref() {
let full_path_string: SharedString = full_path.to_string_lossy().into_owned().into();
let (name, parent) =
extract_file_name_and_directory_from_full_path(full_path, &full_path_string);
let icon_path = FileIcons::get_icon(&full_path, cx);
(name, parent, icon_path)
} else {
("Image".into(), None, None)
};
AddedContext { AddedContext {
kind: ContextKind::Image, kind: ContextKind::Image,
name: "Image".into(), name,
parent: None, parent,
tooltip: None, tooltip: None,
icon_path: None, icon_path,
status: match context.status() { status: match context.status() {
ImageStatus::Loading => ContextStatus::Loading { ImageStatus::Loading => ContextStatus::Loading {
message: "Loading…".into(), message: "Loading…".into(),
@ -639,6 +637,22 @@ impl AddedContext {
} }
} }
fn extract_file_name_and_directory_from_full_path(
path: &Path,
name_fallback: &SharedString,
) -> (SharedString, Option<SharedString>) {
let name = path
.file_name()
.map(|n| n.to_string_lossy().into_owned().into())
.unwrap_or_else(|| name_fallback.clone());
let parent = path
.parent()
.and_then(|p| p.file_name())
.map(|n| n.to_string_lossy().into_owned().into());
(name, parent)
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct ContextFileExcerpt { struct ContextFileExcerpt {
pub file_name_and_range: SharedString, pub file_name_and_range: SharedString,
@ -765,19 +779,25 @@ impl Component for AddedContext {
let mut next_context_id = ContextId::zero(); let mut next_context_id = ContextId::zero();
let image_ready = ( let image_ready = (
"Ready", "Ready",
AddedContext::image(ImageContext { AddedContext::image(
ImageContext {
context_id: next_context_id.post_inc(), context_id: next_context_id.post_inc(),
project_path: None, project_path: None,
full_path: None,
original_image: Arc::new(Image::empty()), original_image: Arc::new(Image::empty()),
image_task: Task::ready(Some(LanguageModelImage::empty())).shared(), image_task: Task::ready(Some(LanguageModelImage::empty())).shared(),
}), },
cx,
),
); );
let image_loading = ( let image_loading = (
"Loading", "Loading",
AddedContext::image(ImageContext { AddedContext::image(
ImageContext {
context_id: next_context_id.post_inc(), context_id: next_context_id.post_inc(),
project_path: None, project_path: None,
full_path: None,
original_image: Arc::new(Image::empty()), original_image: Arc::new(Image::empty()),
image_task: cx image_task: cx
.background_spawn(async move { .background_spawn(async move {
@ -785,17 +805,23 @@ impl Component for AddedContext {
Some(LanguageModelImage::empty()) Some(LanguageModelImage::empty())
}) })
.shared(), .shared(),
}), },
cx,
),
); );
let image_error = ( let image_error = (
"Error", "Error",
AddedContext::image(ImageContext { AddedContext::image(
ImageContext {
context_id: next_context_id.post_inc(), context_id: next_context_id.post_inc(),
project_path: None, project_path: None,
full_path: None,
original_image: Arc::new(Image::empty()), original_image: Arc::new(Image::empty()),
image_task: Task::ready(None).shared(), image_task: Task::ready(None).shared(),
}), },
cx,
),
); );
Some( Some(