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

Umesh Yadav and Bennet Bo Fenner created

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>

Change summary

crates/agent/src/context.rs         |   1 
crates/agent/src/context_store.rs   |  14 ++-
crates/agent/src/ui/context_pill.rs | 116 ++++++++++++++++++------------
3 files changed, 81 insertions(+), 50 deletions(-)

Detailed changes

crates/agent/src/context.rs πŸ”—

@@ -734,6 +734,7 @@ impl Display for RulesContext {
 #[derive(Debug, Clone)]
 pub struct ImageContext {
     pub project_path: Option<ProjectPath>,
+    pub full_path: Option<Arc<Path>>,
     pub original_image: Arc<gpui::Image>,
     // 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`.

crates/agent/src/context_store.rs πŸ”—

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

crates/agent/src/ui/context_pill.rs πŸ”—

@@ -304,7 +304,7 @@ impl AddedContext {
             AgentContextHandle::Thread(handle) => Some(Self::pending_thread(handle, cx)),
             AgentContextHandle::TextThread(handle) => Some(Self::pending_text_thread(handle, 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::TextThread(context) => Self::attached_text_thread(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 {
         let full_path_string: SharedString = full_path.to_string_lossy().into_owned().into();
-        let name = full_path
-            .file_name()
-            .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());
+        let (name, parent) =
+            extract_file_name_and_directory_from_full_path(full_path, &full_path_string);
         AddedContext {
             kind: ContextKind::File,
             name,
@@ -370,14 +364,8 @@ impl AddedContext {
 
     fn directory(handle: DirectoryContextHandle, full_path: &Path) -> AddedContext {
         let full_path_string: SharedString = full_path.to_string_lossy().into_owned().into();
-        let name = full_path
-            .file_name()
-            .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());
+        let (name, parent) =
+            extract_file_name_and_directory_from_full_path(full_path, &full_path_string);
         AddedContext {
             kind: ContextKind::Directory,
             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 {
             kind: ContextKind::Image,
-            name: "Image".into(),
-            parent: None,
+            name,
+            parent,
             tooltip: None,
-            icon_path: None,
+            icon_path,
             status: match context.status() {
                 ImageStatus::Loading => ContextStatus::Loading {
                     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)]
 struct ContextFileExcerpt {
     pub file_name_and_range: SharedString,
@@ -765,37 +779,49 @@ impl Component for AddedContext {
         let mut next_context_id = ContextId::zero();
         let image_ready = (
             "Ready",
-            AddedContext::image(ImageContext {
-                context_id: next_context_id.post_inc(),
-                project_path: None,
-                original_image: Arc::new(Image::empty()),
-                image_task: Task::ready(Some(LanguageModelImage::empty())).shared(),
-            }),
+            AddedContext::image(
+                ImageContext {
+                    context_id: next_context_id.post_inc(),
+                    project_path: None,
+                    full_path: None,
+                    original_image: Arc::new(Image::empty()),
+                    image_task: Task::ready(Some(LanguageModelImage::empty())).shared(),
+                },
+                cx,
+            ),
         );
 
         let image_loading = (
             "Loading",
-            AddedContext::image(ImageContext {
-                context_id: next_context_id.post_inc(),
-                project_path: None,
-                original_image: Arc::new(Image::empty()),
-                image_task: cx
-                    .background_spawn(async move {
-                        smol::Timer::after(Duration::from_secs(60 * 5)).await;
-                        Some(LanguageModelImage::empty())
-                    })
-                    .shared(),
-            }),
+            AddedContext::image(
+                ImageContext {
+                    context_id: next_context_id.post_inc(),
+                    project_path: None,
+                    full_path: None,
+                    original_image: Arc::new(Image::empty()),
+                    image_task: cx
+                        .background_spawn(async move {
+                            smol::Timer::after(Duration::from_secs(60 * 5)).await;
+                            Some(LanguageModelImage::empty())
+                        })
+                        .shared(),
+                },
+                cx,
+            ),
         );
 
         let image_error = (
             "Error",
-            AddedContext::image(ImageContext {
-                context_id: next_context_id.post_inc(),
-                project_path: None,
-                original_image: Arc::new(Image::empty()),
-                image_task: Task::ready(None).shared(),
-            }),
+            AddedContext::image(
+                ImageContext {
+                    context_id: next_context_id.post_inc(),
+                    project_path: None,
+                    full_path: None,
+                    original_image: Arc::new(Image::empty()),
+                    image_task: Task::ready(None).shared(),
+                },
+                cx,
+            ),
         );
 
         Some(