Start displaying messages in new thread element
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This commit is contained in:
@@ -82,8 +82,22 @@ impl acp::Client for AcpClientDelegate {
|
||||
|
||||
async fn stream_message_chunk(
|
||||
&self,
|
||||
chunk: acp::StreamMessageChunkParams,
|
||||
params: acp::StreamMessageChunkParams,
|
||||
) -> Result<acp::StreamMessageChunkResponse> {
|
||||
let cx = &mut self.cx.clone();
|
||||
|
||||
cx.update(|cx| {
|
||||
self.update_thread(¶ms.thread_id.into(), cx, |thread, cx| {
|
||||
let acp::MessageChunk::Text { chunk } = ¶ms.chunk;
|
||||
thread.push_assistant_chunk(
|
||||
MessageChunk::Text {
|
||||
chunk: chunk.into(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
})?;
|
||||
|
||||
Ok(acp::StreamMessageChunkResponse)
|
||||
}
|
||||
|
||||
|
||||
@@ -219,6 +219,28 @@ impl Thread {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn push_assistant_chunk(&mut self, chunk: MessageChunk, cx: &mut Context<Self>) {
|
||||
if let Some(last_entry) = self.entries.last_mut() {
|
||||
if let AgentThreadEntryContent::Message(Message {
|
||||
ref mut chunks,
|
||||
role: Role::Assistant,
|
||||
}) = last_entry.content
|
||||
{
|
||||
chunks.push(chunk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.entries.push(ThreadEntry {
|
||||
id: self.next_entry_id.post_inc(),
|
||||
content: AgentThreadEntryContent::Message(Message {
|
||||
role: Role::Assistant,
|
||||
chunks: vec![chunk],
|
||||
}),
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn send(&mut self, message: Message, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||
let agent = self.agent.clone();
|
||||
let id = self.id.clone();
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use editor::{Editor, MultiBuffer};
|
||||
use gpui::{App, Entity, Focusable, SharedString, Window, div, prelude::*};
|
||||
use gpui::{App, Entity, Focusable, SharedString, Subscription, Window, div, prelude::*};
|
||||
use gpui::{FocusHandle, Task};
|
||||
use language::Buffer;
|
||||
use ui::Tooltip;
|
||||
use ui::prelude::*;
|
||||
use zed_actions::agent::Chat;
|
||||
|
||||
use crate::{Message, MessageChunk, Role, Thread};
|
||||
use crate::{AgentThreadEntryContent, Message, MessageChunk, Role, Thread, ThreadEntry};
|
||||
|
||||
pub struct ThreadElement {
|
||||
thread: Entity<Thread>,
|
||||
// todo! use full message editor from agent2
|
||||
message_editor: Entity<Editor>,
|
||||
send_task: Option<Task<Result<()>>>,
|
||||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
impl ThreadElement {
|
||||
@@ -39,10 +38,15 @@ impl ThreadElement {
|
||||
editor
|
||||
});
|
||||
|
||||
let subscription = cx.observe(&thread, |_, _, cx| {
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
Self {
|
||||
thread,
|
||||
message_editor,
|
||||
send_task: None,
|
||||
_subscription: subscription,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,18 +64,48 @@ impl ThreadElement {
|
||||
return;
|
||||
}
|
||||
|
||||
self.send_task = Some(self.thread.update(cx, |thread, cx| {
|
||||
let task = self.thread.update(cx, |thread, cx| {
|
||||
let message = Message {
|
||||
role: Role::User,
|
||||
chunks: vec![MessageChunk::Text { chunk: text.into() }],
|
||||
};
|
||||
thread.send(message, cx)
|
||||
});
|
||||
|
||||
self.send_task = Some(cx.spawn(async move |this, cx| {
|
||||
task.await?;
|
||||
|
||||
this.update(cx, |this, _cx| {
|
||||
this.send_task.take();
|
||||
})
|
||||
}));
|
||||
|
||||
self.message_editor.update(cx, |editor, cx| {
|
||||
editor.clear(window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn render_entry(
|
||||
&self,
|
||||
entry: &ThreadEntry,
|
||||
_window: &mut Window,
|
||||
_cx: &Context<Self>,
|
||||
) -> AnyElement {
|
||||
match &entry.content {
|
||||
AgentThreadEntryContent::Message(message) => div()
|
||||
.children(message.chunks.iter().map(|chunk| match chunk {
|
||||
MessageChunk::Text { chunk } => div().child(chunk.clone()),
|
||||
_ => todo!(),
|
||||
}))
|
||||
.into_any(),
|
||||
AgentThreadEntryContent::ReadFile { path, content: _ } => {
|
||||
// todo!
|
||||
div()
|
||||
.child(format!("<Reading file {}>", path.display()))
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for ThreadElement {
|
||||
@@ -81,7 +115,7 @@ impl Focusable for ThreadElement {
|
||||
}
|
||||
|
||||
impl Render for ThreadElement {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let text = self.message_editor.read(cx).text(cx);
|
||||
let is_editor_empty = text.is_empty();
|
||||
let focus_handle = self.message_editor.focus_handle(cx);
|
||||
@@ -89,7 +123,22 @@ impl Render for ThreadElement {
|
||||
v_flex()
|
||||
.key_context("MessageEditor")
|
||||
.on_action(cx.listener(Self::chat))
|
||||
.child(div().h_full())
|
||||
.child(
|
||||
v_flex().h_full().gap_1().children(
|
||||
self.thread
|
||||
.read(cx)
|
||||
.entries()
|
||||
.iter()
|
||||
.map(|entry| self.render_entry(entry, window, cx)),
|
||||
),
|
||||
)
|
||||
.when(self.send_task.is_some(), |this| {
|
||||
this.child(
|
||||
Label::new("Generating...")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
|
||||
Reference in New Issue
Block a user