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}