thread_element.rs

  1use std::sync::Arc;
  2
  3use anyhow::Result;
  4use editor::{Editor, MultiBuffer};
  5use gpui::{App, Entity, Focusable, SharedString, Window, div, prelude::*};
  6use gpui::{FocusHandle, Task};
  7use language::Buffer;
  8use ui::Tooltip;
  9use ui::prelude::*;
 10use zed_actions::agent::Chat;
 11
 12use crate::{Message, MessageChunk, Role, Thread};
 13
 14pub struct ThreadElement {
 15    thread: Entity<Thread>,
 16    // todo! use full message editor from agent2
 17    message_editor: Entity<Editor>,
 18    send_task: Option<Task<Result<()>>>,
 19}
 20
 21impl ThreadElement {
 22    pub fn new(thread: Entity<Thread>, window: &mut Window, cx: &mut Context<Self>) -> Self {
 23        let message_editor = cx.new(|cx| {
 24            let buffer = cx.new(|cx| Buffer::local("", cx));
 25            let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 26
 27            let mut editor = Editor::new(
 28                editor::EditorMode::AutoHeight {
 29                    min_lines: 5,
 30                    max_lines: None,
 31                },
 32                buffer,
 33                None,
 34                window,
 35                cx,
 36            );
 37            editor.set_placeholder_text("Send a message", cx);
 38            editor.set_soft_wrap();
 39            editor
 40        });
 41
 42        Self {
 43            thread,
 44            message_editor,
 45            send_task: None,
 46        }
 47    }
 48
 49    pub fn title(&self, cx: &App) -> SharedString {
 50        self.thread.read(cx).title()
 51    }
 52
 53    pub fn cancel(&mut self) {
 54        self.send_task.take();
 55    }
 56
 57    fn chat(&mut self, _: &Chat, window: &mut Window, cx: &mut Context<Self>) {
 58        let text = self.message_editor.read(cx).text(cx);
 59        if text.is_empty() {
 60            return;
 61        }
 62
 63        self.send_task = Some(self.thread.update(cx, |thread, cx| {
 64            let message = Message {
 65                role: Role::User,
 66                chunks: vec![MessageChunk::Text { chunk: text.into() }],
 67            };
 68            thread.send(message, cx)
 69        }));
 70
 71        self.message_editor.update(cx, |editor, cx| {
 72            editor.clear(window, cx);
 73        });
 74    }
 75}
 76
 77impl Focusable for ThreadElement {
 78    fn focus_handle(&self, cx: &App) -> FocusHandle {
 79        self.message_editor.focus_handle(cx)
 80    }
 81}
 82
 83impl Render for ThreadElement {
 84    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 85        let text = self.message_editor.read(cx).text(cx);
 86        let is_editor_empty = text.is_empty();
 87        let focus_handle = self.message_editor.focus_handle(cx);
 88
 89        v_flex()
 90            .key_context("MessageEditor")
 91            .on_action(cx.listener(Self::chat))
 92            .child(div().h_full())
 93            .child(
 94                div()
 95                    .bg(cx.theme().colors().editor_background)
 96                    .border_t_1()
 97                    .border_color(cx.theme().colors().border)
 98                    .p_2()
 99                    .child(self.message_editor.clone()),
100            )
101            .child(
102                h_flex().p_2().justify_end().child(
103                    IconButton::new("send-message", IconName::Send)
104                        .icon_color(Color::Accent)
105                        .style(ButtonStyle::Filled)
106                        .disabled(is_editor_empty)
107                        .on_click({
108                            let focus_handle = focus_handle.clone();
109                            move |_event, window, cx| {
110                                focus_handle.dispatch_action(&Chat, window, cx);
111                            }
112                        })
113                        .when(!is_editor_empty, |button| {
114                            button.tooltip(move |window, cx| {
115                                Tooltip::for_action("Send", &Chat, window, cx)
116                            })
117                        })
118                        .when(is_editor_empty, |button| {
119                            button.tooltip(Tooltip::text("Type a message to submit"))
120                        }),
121                ),
122            )
123    }
124}