assistant2: Add thread history (#21599)
This PR adds support for thread history to the Assistant 2 panel. We also now generate summaries for the threads. <img width="986" alt="Screenshot 2024-12-05 at 12 56 53 PM" src="https://github.com/user-attachments/assets/46cb1309-38a2-4ab9-9fcc-c1275d4b5f2c"> <img width="986" alt="Screenshot 2024-12-05 at 12 56 58 PM" src="https://github.com/user-attachments/assets/8c91ba57-a6c5-4b88-be05-b22fb615ece5"> Release Notes: - N/A --------- Co-authored-by: Piotr <piotr@zed.dev>
This commit is contained in:
parent
2d43ad12e6
commit
787c75cbda
8 changed files with 375 additions and 127 deletions
144
crates/assistant2/src/thread_history.rs
Normal file
144
crates/assistant2/src/thread_history.rs
Normal file
|
@ -0,0 +1,144 @@
|
|||
use gpui::{
|
||||
uniform_list, AppContext, FocusHandle, FocusableView, Model, UniformListScrollHandle, WeakView,
|
||||
};
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
use ui::{prelude::*, IconButtonShape, ListItem};
|
||||
|
||||
use crate::thread::Thread;
|
||||
use crate::thread_store::ThreadStore;
|
||||
use crate::AssistantPanel;
|
||||
|
||||
pub struct ThreadHistory {
|
||||
focus_handle: FocusHandle,
|
||||
assistant_panel: WeakView<AssistantPanel>,
|
||||
thread_store: Model<ThreadStore>,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
}
|
||||
|
||||
impl ThreadHistory {
|
||||
pub(crate) fn new(
|
||||
assistant_panel: WeakView<AssistantPanel>,
|
||||
thread_store: Model<ThreadStore>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
assistant_panel,
|
||||
thread_store,
|
||||
scroll_handle: UniformListScrollHandle::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FocusableView for ThreadHistory {
|
||||
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ThreadHistory {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let threads = self.thread_store.update(cx, |this, cx| this.threads(cx));
|
||||
|
||||
v_flex()
|
||||
.id("thread-history-container")
|
||||
.track_focus(&self.focus_handle)
|
||||
.overflow_y_scroll()
|
||||
.size_full()
|
||||
.p_1()
|
||||
.map(|history| {
|
||||
if threads.is_empty() {
|
||||
history
|
||||
.justify_center()
|
||||
.child(
|
||||
h_flex().w_full().justify_center().child(
|
||||
Label::new("You don't have any past threads yet.")
|
||||
.size(LabelSize::Small),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
history.child(
|
||||
uniform_list(
|
||||
cx.view().clone(),
|
||||
"thread-history",
|
||||
threads.len(),
|
||||
move |history, range, _cx| {
|
||||
threads[range]
|
||||
.iter()
|
||||
.map(|thread| {
|
||||
PastThread::new(
|
||||
thread.clone(),
|
||||
history.assistant_panel.clone(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.flex_grow(),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct PastThread {
|
||||
thread: Model<Thread>,
|
||||
assistant_panel: WeakView<AssistantPanel>,
|
||||
}
|
||||
|
||||
impl PastThread {
|
||||
pub fn new(thread: Model<Thread>, assistant_panel: WeakView<AssistantPanel>) -> Self {
|
||||
Self {
|
||||
thread,
|
||||
assistant_panel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for PastThread {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let (id, summary) = {
|
||||
const DEFAULT_SUMMARY: SharedString = SharedString::new_static("New Thread");
|
||||
let thread = self.thread.read(cx);
|
||||
(
|
||||
thread.id().clone(),
|
||||
thread.summary().unwrap_or(DEFAULT_SUMMARY),
|
||||
)
|
||||
};
|
||||
|
||||
let thread_timestamp = time_format::format_localized_timestamp(
|
||||
OffsetDateTime::from_unix_timestamp(self.thread.read(cx).updated_at().timestamp())
|
||||
.unwrap(),
|
||||
OffsetDateTime::now_utc(),
|
||||
self.assistant_panel
|
||||
.update(cx, |this, _cx| this.local_timezone())
|
||||
.unwrap_or(UtcOffset::UTC),
|
||||
time_format::TimestampFormat::EnhancedAbsolute,
|
||||
);
|
||||
ListItem::new(("past-thread", self.thread.entity_id()))
|
||||
.start_slot(Icon::new(IconName::MessageBubbles))
|
||||
.child(Label::new(summary))
|
||||
.end_slot(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(Label::new(thread_timestamp).color(Color::Disabled))
|
||||
.child(
|
||||
IconButton::new("delete", IconName::TrashAlt)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small),
|
||||
),
|
||||
)
|
||||
.on_click({
|
||||
let assistant_panel = self.assistant_panel.clone();
|
||||
move |_event, cx| {
|
||||
assistant_panel
|
||||
.update(cx, |this, cx| {
|
||||
this.open_thread(&id, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue