diff --git a/assets/icons/eraser.svg b/assets/icons/eraser.svg
new file mode 100644
index 0000000000..edb893a8c6
--- /dev/null
+++ b/assets/icons/eraser.svg
@@ -0,0 +1,4 @@
+
diff --git a/crates/assistant2/src/active_thread.rs b/crates/assistant2/src/active_thread.rs
index d9cd8fcc46..bf0086ee11 100644
--- a/crates/assistant2/src/active_thread.rs
+++ b/crates/assistant2/src/active_thread.rs
@@ -15,6 +15,7 @@ use ui::prelude::*;
use workspace::Workspace;
use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent};
+use crate::ui::ContextPill;
pub struct ActiveThread {
workspace: WeakView,
@@ -202,6 +203,8 @@ impl ActiveThread {
return Empty.into_any();
};
+ let context = self.thread.read(cx).context_for_message(message_id);
+
let (role_icon, role_name) = match message.role {
Role::User => (IconName::Person, "You"),
Role::Assistant => (IconName::ZedAssistant, "Assistant"),
@@ -229,7 +232,16 @@ impl ActiveThread {
.child(Label::new(role_name).size(LabelSize::Small)),
),
)
- .child(v_flex().p_1p5().text_ui(cx).child(markdown.clone())),
+ .child(v_flex().p_1p5().text_ui(cx).child(markdown.clone()))
+ .when_some(context, |parent, context| {
+ parent.child(
+ h_flex().flex_wrap().gap_2().p_1p5().children(
+ context
+ .iter()
+ .map(|context| ContextPill::new(context.clone())),
+ ),
+ )
+ }),
)
.into_any()
}
diff --git a/crates/assistant2/src/assistant.rs b/crates/assistant2/src/assistant.rs
index 3c8520680e..d83a4f1818 100644
--- a/crates/assistant2/src/assistant.rs
+++ b/crates/assistant2/src/assistant.rs
@@ -1,10 +1,12 @@
mod active_thread;
mod assistant_panel;
+mod context;
mod context_picker;
mod message_editor;
mod thread;
mod thread_history;
mod thread_store;
+mod ui;
use command_palette_hooks::CommandPaletteFilter;
use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
diff --git a/crates/assistant2/src/context.rs b/crates/assistant2/src/context.rs
new file mode 100644
index 0000000000..c865139ce3
--- /dev/null
+++ b/crates/assistant2/src/context.rs
@@ -0,0 +1,14 @@
+use gpui::SharedString;
+
+/// Some context attached to a message in a thread.
+#[derive(Debug, Clone)]
+pub struct Context {
+ pub name: SharedString,
+ pub kind: ContextKind,
+ pub text: SharedString,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ContextKind {
+ File,
+}
diff --git a/crates/assistant2/src/message_editor.rs b/crates/assistant2/src/message_editor.rs
index e0d620c2c1..d5e778f510 100644
--- a/crates/assistant2/src/message_editor.rs
+++ b/crates/assistant2/src/message_editor.rs
@@ -10,19 +10,28 @@ use ui::{
PopoverMenuHandle, Tooltip,
};
+use crate::context::{Context, ContextKind};
use crate::context_picker::{ContextPicker, ContextPickerDelegate};
use crate::thread::{RequestKind, Thread};
+use crate::ui::ContextPill;
use crate::{Chat, ToggleModelSelector};
pub struct MessageEditor {
thread: Model,
editor: View,
+ context: Vec,
pub(crate) context_picker_handle: PopoverMenuHandle>,
use_tools: bool,
}
impl MessageEditor {
pub fn new(thread: Model, cx: &mut ViewContext) -> Self {
+ let mocked_context = vec![Context {
+ name: "shape.rs".into(),
+ kind: ContextKind::File,
+ text: "```rs\npub enum Shape {\n Circle,\n Square,\n Triangle,\n}".into(),
+ }];
+
Self {
thread,
editor: cx.new_view(|cx| {
@@ -31,6 +40,7 @@ impl MessageEditor {
editor
}),
+ context: mocked_context,
context_picker_handle: PopoverMenuHandle::default(),
use_tools: false,
}
@@ -62,9 +72,10 @@ impl MessageEditor {
editor.clear(cx);
text
});
+ let context = self.context.drain(..).collect::>();
self.thread.update(cx, |thread, cx| {
- thread.insert_user_message(user_message, cx);
+ thread.insert_user_message(user_message, context, cx);
let mut request = thread.to_completion_request(request_kind, cx);
if self.use_tools {
@@ -158,12 +169,32 @@ impl Render for MessageEditor {
.p_2()
.bg(cx.theme().colors().editor_background)
.child(
- h_flex().gap_2().child(ContextPicker::new(
- cx.view().downgrade(),
- IconButton::new("add-context", IconName::Plus)
- .shape(IconButtonShape::Square)
- .icon_size(IconSize::Small),
- )),
+ h_flex()
+ .flex_wrap()
+ .gap_2()
+ .child(ContextPicker::new(
+ cx.view().downgrade(),
+ IconButton::new("add-context", IconName::Plus)
+ .shape(IconButtonShape::Square)
+ .icon_size(IconSize::Small),
+ ))
+ .children(
+ self.context
+ .iter()
+ .map(|context| ContextPill::new(context.clone())),
+ )
+ .when(!self.context.is_empty(), |parent| {
+ parent.child(
+ IconButton::new("remove-all-context", IconName::Eraser)
+ .shape(IconButtonShape::Square)
+ .icon_size(IconSize::Small)
+ .tooltip(move |cx| Tooltip::text("Remove All Context", cx))
+ .on_click(cx.listener(|this, _event, cx| {
+ this.context.clear();
+ cx.notify();
+ })),
+ )
+ }),
)
.child({
let settings = ThemeSettings::get_global(cx);
diff --git a/crates/assistant2/src/thread.rs b/crates/assistant2/src/thread.rs
index 833f8c9b03..77c0cd9836 100644
--- a/crates/assistant2/src/thread.rs
+++ b/crates/assistant2/src/thread.rs
@@ -17,6 +17,8 @@ use serde::{Deserialize, Serialize};
use util::{post_inc, TryFutureExt as _};
use uuid::Uuid;
+use crate::context::{Context, ContextKind};
+
#[derive(Debug, Clone, Copy)]
pub enum RequestKind {
Chat,
@@ -62,6 +64,7 @@ pub struct Thread {
pending_summary: Task