@@ -120,22 +120,32 @@ pub fn init(cx: &mut App) {
.register_action(|workspace, _: &OpenAgentDiff, window, cx| {
if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
workspace.focus_panel::<AgentPanel>(window, cx);
- let thread = panel.read(cx).thread.read(cx).thread().clone();
- AgentDiffPane::deploy_in_workspace(thread, workspace, window, cx);
+ match &panel.read(cx).active_view {
+ ActiveView::Thread { thread, .. } => {
+ let thread = thread.read(cx).thread().clone();
+ AgentDiffPane::deploy_in_workspace(thread, workspace, window, cx);
+ }
+ ActiveView::TextThread { .. }
+ | ActiveView::History
+ | ActiveView::Configuration => {}
+ }
}
})
.register_action(|workspace, _: &Follow, window, cx| {
workspace.follow(CollaboratorId::Agent, window, cx);
})
.register_action(|workspace, _: &ExpandMessageEditor, window, cx| {
- if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
- workspace.focus_panel::<AgentPanel>(window, cx);
- panel.update(cx, |panel, cx| {
- panel.message_editor.update(cx, |editor, cx| {
+ let Some(panel) = workspace.panel::<AgentPanel>(cx) else {
+ return;
+ };
+ workspace.focus_panel::<AgentPanel>(window, cx);
+ panel.update(cx, |panel, cx| {
+ if let Some(message_editor) = panel.active_message_editor() {
+ message_editor.update(cx, |editor, cx| {
editor.expand_message_editor(&ExpandMessageEditor, window, cx);
});
- });
- }
+ }
+ });
})
.register_action(|workspace, _: &ToggleNavigationMenu, window, cx| {
if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
@@ -173,8 +183,9 @@ pub fn init(cx: &mut App) {
enum ActiveView {
Thread {
+ thread: Entity<ActiveThread>,
change_title_editor: Entity<Editor>,
- thread: WeakEntity<Thread>,
+ message_editor: Entity<MessageEditor>,
_subscriptions: Vec<gpui::Subscription>,
},
TextThread {
@@ -202,8 +213,13 @@ impl ActiveView {
}
}
- pub fn thread(thread: Entity<Thread>, window: &mut Window, cx: &mut App) -> Self {
- let summary = thread.read(cx).summary().or_default();
+ pub fn thread(
+ active_thread: Entity<ActiveThread>,
+ message_editor: Entity<MessageEditor>,
+ window: &mut Window,
+ cx: &mut Context<AgentPanel>,
+ ) -> Self {
+ let summary = active_thread.read(cx).summary(cx).or_default();
let editor = cx.new(|cx| {
let mut editor = Editor::single_line(window, cx);
@@ -212,20 +228,37 @@ impl ActiveView {
});
let subscriptions = vec![
+ cx.subscribe(&message_editor, |this, _, event, cx| match event {
+ MessageEditorEvent::Changed | MessageEditorEvent::EstimatedTokenCount => {
+ cx.notify();
+ }
+ MessageEditorEvent::ScrollThreadToBottom => match &this.active_view {
+ ActiveView::Thread { thread, .. } => {
+ thread.update(cx, |thread, cx| {
+ thread.scroll_to_bottom(cx);
+ });
+ }
+ ActiveView::TextThread { .. }
+ | ActiveView::History
+ | ActiveView::Configuration => {}
+ },
+ }),
window.subscribe(&editor, cx, {
{
- let thread = thread.clone();
+ let thread = active_thread.clone();
move |editor, event, window, cx| match event {
EditorEvent::BufferEdited => {
let new_summary = editor.read(cx).text(cx);
thread.update(cx, |thread, cx| {
- thread.set_summary(new_summary, cx);
+ thread.thread().update(cx, |thread, cx| {
+ thread.set_summary(new_summary, cx);
+ });
})
}
EditorEvent::Blurred => {
if editor.read(cx).text(cx).is_empty() {
- let summary = thread.read(cx).summary().or_default();
+ let summary = thread.read(cx).summary(cx).or_default();
editor.update(cx, |editor, cx| {
editor.set_text(summary, window, cx);
@@ -236,9 +269,14 @@ impl ActiveView {
}
}
}),
- window.subscribe(&thread, cx, {
+ cx.subscribe(&active_thread, |_, _, event, cx| match &event {
+ ActiveThreadEvent::EditingMessageTokenCountChanged => {
+ cx.notify();
+ }
+ }),
+ cx.subscribe_in(&active_thread.read(cx).thread().clone(), window, {
let editor = editor.clone();
- move |thread, event, window, cx| match event {
+ move |_, thread, event, window, cx| match event {
ThreadEvent::SummaryGenerated => {
let summary = thread.read(cx).summary().or_default();
@@ -246,6 +284,9 @@ impl ActiveView {
editor.set_text(summary, window, cx);
})
}
+ ThreadEvent::MessageAdded(_) => {
+ cx.notify();
+ }
_ => {}
}
}),
@@ -253,7 +294,8 @@ impl ActiveView {
Self::Thread {
change_title_editor: editor,
- thread: thread.downgrade(),
+ thread: active_thread,
+ message_editor: message_editor,
_subscriptions: subscriptions,
}
}
@@ -366,9 +408,6 @@ pub struct AgentPanel {
fs: Arc<dyn Fs>,
language_registry: Arc<LanguageRegistry>,
thread_store: Entity<ThreadStore>,
- thread: Entity<ActiveThread>,
- message_editor: Entity<MessageEditor>,
- _active_thread_subscriptions: Vec<Subscription>,
_default_model_subscription: Subscription,
context_store: Entity<TextThreadStore>,
prompt_store: Option<Entity<PromptStore>>,
@@ -513,18 +552,6 @@ impl AgentPanel {
)
});
- let message_editor_subscription =
- cx.subscribe(&message_editor, |this, _, event, cx| match event {
- MessageEditorEvent::Changed | MessageEditorEvent::EstimatedTokenCount => {
- cx.notify();
- }
- MessageEditorEvent::ScrollThreadToBottom => {
- this.thread.update(cx, |thread, cx| {
- thread.scroll_to_bottom(cx);
- });
- }
- });
-
let thread_id = thread.read(cx).id().clone();
let history_store = cx.new(|cx| {
HistoryStore::new(
@@ -537,9 +564,22 @@ impl AgentPanel {
cx.observe(&history_store, |_, _, cx| cx.notify()).detach();
+ let active_thread = cx.new(|cx| {
+ ActiveThread::new(
+ thread.clone(),
+ thread_store.clone(),
+ context_store.clone(),
+ message_editor_context_store.clone(),
+ language_registry.clone(),
+ workspace.clone(),
+ window,
+ cx,
+ )
+ });
+
let panel_type = AgentSettings::get_global(cx).default_view;
let active_view = match panel_type {
- DefaultView::Thread => ActiveView::thread(thread.clone(), window, cx),
+ DefaultView::Thread => ActiveView::thread(active_thread, message_editor, window, cx),
DefaultView::TextThread => {
let context =
context_store.update(cx, |context_store, cx| context_store.create(cx));
@@ -567,33 +607,8 @@ impl AgentPanel {
}
};
- let thread_subscription = cx.subscribe(&thread, |_, _, event, cx| {
- if let ThreadEvent::MessageAdded(_) = &event {
- // needed to leave empty state
- cx.notify();
- }
- });
- let active_thread = cx.new(|cx| {
- ActiveThread::new(
- thread.clone(),
- thread_store.clone(),
- context_store.clone(),
- message_editor_context_store.clone(),
- language_registry.clone(),
- workspace.clone(),
- window,
- cx,
- )
- });
AgentDiff::set_active_thread(&workspace, &thread, window, cx);
- let active_thread_subscription =
- cx.subscribe(&active_thread, |_, _, event, cx| match &event {
- ActiveThreadEvent::EditingMessageTokenCountChanged => {
- cx.notify();
- }
- });
-
let weak_panel = weak_self.clone();
window.defer(cx, move |window, cx| {
@@ -630,13 +645,18 @@ impl AgentPanel {
let _default_model_subscription = cx.subscribe(
&LanguageModelRegistry::global(cx),
|this, _, event: &language_model::Event, cx| match event {
- language_model::Event::DefaultModelChanged => {
- this.thread
- .read(cx)
- .thread()
- .clone()
- .update(cx, |thread, cx| thread.get_or_init_configured_model(cx));
- }
+ language_model::Event::DefaultModelChanged => match &this.active_view {
+ ActiveView::Thread { thread, .. } => {
+ thread
+ .read(cx)
+ .thread()
+ .clone()
+ .update(cx, |thread, cx| thread.get_or_init_configured_model(cx));
+ }
+ ActiveView::TextThread { .. }
+ | ActiveView::History
+ | ActiveView::Configuration => {}
+ },
_ => {}
},
);
@@ -649,13 +669,6 @@ impl AgentPanel {
fs: fs.clone(),
language_registry,
thread_store: thread_store.clone(),
- thread: active_thread,
- message_editor,
- _active_thread_subscriptions: vec![
- thread_subscription,
- active_thread_subscription,
- message_editor_subscription,
- ],
_default_model_subscription,
context_store,
prompt_store,
@@ -716,24 +729,34 @@ impl AgentPanel {
}
fn cancel(&mut self, _: &editor::actions::Cancel, window: &mut Window, cx: &mut Context<Self>) {
- self.thread
- .update(cx, |thread, cx| thread.cancel_last_completion(window, cx));
+ match &self.active_view {
+ ActiveView::Thread { thread, .. } => {
+ thread.update(cx, |thread, cx| thread.cancel_last_completion(window, cx));
+ }
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => {}
+ }
+ }
+
+ fn active_message_editor(&self) -> Option<&Entity<MessageEditor>> {
+ match &self.active_view {
+ ActiveView::Thread { message_editor, .. } => Some(message_editor),
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => None,
+ }
}
fn new_thread(&mut self, action: &NewThread, window: &mut Window, cx: &mut Context<Self>) {
// Preserve chat box text when using creating new thread from summary'
- let preserved_text = action
- .from_thread_id
- .is_some()
- .then(|| self.message_editor.read(cx).get_text(cx).trim().to_string());
+ let preserved_text = if action.from_thread_id.is_some() {
+ self.active_message_editor()
+ .map(|editor| editor.read(cx).get_text(cx).trim().to_string())
+ } else {
+ None
+ };
let thread = self
.thread_store
.update(cx, |this, cx| this.create_thread(cx));
- let thread_view = ActiveView::thread(thread.clone(), window, cx);
- self.set_active_view(thread_view, window, cx);
-
let context_store = cx.new(|_cx| {
ContextStore::new(
self.project.downgrade(),
@@ -761,14 +784,7 @@ impl AgentPanel {
.detach_and_log_err(cx);
}
- let thread_subscription = cx.subscribe(&thread, |_, _, event, cx| {
- if let ThreadEvent::MessageAdded(_) = &event {
- // needed to leave empty state
- cx.notify();
- }
- });
-
- self.thread = cx.new(|cx| {
+ let active_thread = cx.new(|cx| {
ActiveThread::new(
thread.clone(),
self.thread_store.clone(),
@@ -780,55 +796,34 @@ impl AgentPanel {
cx,
)
});
- AgentDiff::set_active_thread(&self.workspace, &thread, window, cx);
- let active_thread_subscription =
- cx.subscribe(&self.thread, |_, _, event, cx| match &event {
- ActiveThreadEvent::EditingMessageTokenCountChanged => {
- cx.notify();
- }
- });
-
- self.message_editor = cx.new(|cx| {
+ let message_editor = cx.new(|cx| {
MessageEditor::new(
self.fs.clone(),
self.workspace.clone(),
self.user_store.clone(),
- context_store,
+ context_store.clone(),
self.prompt_store.clone(),
self.thread_store.downgrade(),
self.context_store.downgrade(),
- thread,
+ thread.clone(),
window,
cx,
)
});
if let Some(text) = preserved_text {
- self.message_editor.update(cx, |editor, cx| {
+ message_editor.update(cx, |editor, cx| {
editor.set_text(text, window, cx);
});
}
- self.message_editor.focus_handle(cx).focus(window);
+ message_editor.focus_handle(cx).focus(window);
- let message_editor_subscription =
- cx.subscribe(&self.message_editor, |this, _, event, cx| match event {
- MessageEditorEvent::Changed | MessageEditorEvent::EstimatedTokenCount => {
- cx.notify();
- }
- MessageEditorEvent::ScrollThreadToBottom => {
- this.thread.update(cx, |thread, cx| {
- thread.scroll_to_bottom(cx);
- });
- }
- });
+ let thread_view = ActiveView::thread(active_thread.clone(), message_editor, window, cx);
+ self.set_active_view(thread_view, window, cx);
- self._active_thread_subscriptions = vec![
- thread_subscription,
- active_thread_subscription,
- message_editor_subscription,
- ];
+ AgentDiff::set_active_thread(&self.workspace, &thread, window, cx);
}
fn new_prompt_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -980,22 +975,14 @@ impl AgentPanel {
window: &mut Window,
cx: &mut Context<Self>,
) {
- let thread_view = ActiveView::thread(thread.clone(), window, cx);
- self.set_active_view(thread_view, window, cx);
let context_store = cx.new(|_cx| {
ContextStore::new(
self.project.downgrade(),
Some(self.thread_store.downgrade()),
)
});
- let thread_subscription = cx.subscribe(&thread, |_, _, event, cx| {
- if let ThreadEvent::MessageAdded(_) = &event {
- // needed to leave empty state
- cx.notify();
- }
- });
- self.thread = cx.new(|cx| {
+ let active_thread = cx.new(|cx| {
ActiveThread::new(
thread.clone(),
self.thread_store.clone(),
@@ -1007,16 +994,7 @@ impl AgentPanel {
cx,
)
});
- AgentDiff::set_active_thread(&self.workspace, &thread, window, cx);
-
- let active_thread_subscription =
- cx.subscribe(&self.thread, |_, _, event, cx| match &event {
- ActiveThreadEvent::EditingMessageTokenCountChanged => {
- cx.notify();
- }
- });
-
- self.message_editor = cx.new(|cx| {
+ let message_editor = cx.new(|cx| {
MessageEditor::new(
self.fs.clone(),
self.workspace.clone(),
@@ -1025,30 +1003,16 @@ impl AgentPanel {
self.prompt_store.clone(),
self.thread_store.downgrade(),
self.context_store.downgrade(),
- thread,
+ thread.clone(),
window,
cx,
)
});
- self.message_editor.focus_handle(cx).focus(window);
+ message_editor.focus_handle(cx).focus(window);
- let message_editor_subscription =
- cx.subscribe(&self.message_editor, |this, _, event, cx| match event {
- MessageEditorEvent::Changed | MessageEditorEvent::EstimatedTokenCount => {
- cx.notify();
- }
- MessageEditorEvent::ScrollThreadToBottom => {
- this.thread.update(cx, |thread, cx| {
- thread.scroll_to_bottom(cx);
- });
- }
- });
-
- self._active_thread_subscriptions = vec![
- thread_subscription,
- active_thread_subscription,
- message_editor_subscription,
- ];
+ let thread_view = ActiveView::thread(active_thread.clone(), message_editor, window, cx);
+ self.set_active_view(thread_view, window, cx);
+ AgentDiff::set_active_thread(&self.workspace, &thread, window, cx);
}
pub fn go_back(&mut self, _: &workspace::GoBack, window: &mut Window, cx: &mut Context<Self>) {
@@ -1058,18 +1022,14 @@ impl AgentPanel {
self.active_view = previous_view;
match &self.active_view {
- ActiveView::Thread { .. } => {
- self.message_editor.focus_handle(cx).focus(window);
+ ActiveView::Thread { message_editor, .. } => {
+ message_editor.focus_handle(cx).focus(window);
}
ActiveView::TextThread { context_editor, .. } => {
context_editor.focus_handle(cx).focus(window);
}
- _ => {}
+ ActiveView::History | ActiveView::Configuration => {}
}
- } else {
- self.active_view =
- ActiveView::thread(self.thread.read(cx).thread().clone(), window, cx);
- self.message_editor.focus_handle(cx).focus(window);
}
cx.notify();
}
@@ -1175,12 +1135,17 @@ impl AgentPanel {
window: &mut Window,
cx: &mut Context<Self>,
) {
- let thread = self.thread.read(cx).thread().clone();
- self.workspace
- .update(cx, |workspace, cx| {
- AgentDiffPane::deploy_in_workspace(thread, workspace, window, cx)
- })
- .log_err();
+ match &self.active_view {
+ ActiveView::Thread { thread, .. } => {
+ let thread = thread.read(cx).thread().clone();
+ self.workspace
+ .update(cx, |workspace, cx| {
+ AgentDiffPane::deploy_in_workspace(thread, workspace, window, cx)
+ })
+ .log_err();
+ }
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => {}
+ }
}
pub(crate) fn open_configuration(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -1222,12 +1187,18 @@ impl AgentPanel {
return;
};
- let Some(thread) = self.active_thread() else {
- return;
- };
-
- active_thread::open_active_thread_as_markdown(thread, workspace, window, cx)
- .detach_and_log_err(cx);
+ match &self.active_view {
+ ActiveView::Thread { thread, .. } => {
+ active_thread::open_active_thread_as_markdown(
+ thread.read(cx).thread().clone(),
+ workspace,
+ window,
+ cx,
+ )
+ .detach_and_log_err(cx);
+ }
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => {}
+ }
}
fn handle_agent_configuration_event(
@@ -1257,9 +1228,9 @@ impl AgentPanel {
}
}
- pub(crate) fn active_thread(&self) -> Option<Entity<Thread>> {
+ pub(crate) fn active_thread(&self, cx: &App) -> Option<Entity<Thread>> {
match &self.active_view {
- ActiveView::Thread { thread, .. } => thread.upgrade(),
+ ActiveView::Thread { thread, .. } => Some(thread.read(cx).thread().clone()),
_ => None,
}
}
@@ -1273,19 +1244,19 @@ impl AgentPanel {
.update(cx, |this, cx| this.delete_thread(thread_id, cx))
}
- pub(crate) fn has_active_thread(&self) -> bool {
- matches!(self.active_view, ActiveView::Thread { .. })
- }
-
fn continue_conversation(&mut self, window: &mut Window, cx: &mut Context<Self>) {
- let thread_state = self.thread.read(cx).thread().read(cx);
+ let ActiveView::Thread { thread, .. } = &self.active_view else {
+ return;
+ };
+
+ let thread_state = thread.read(cx).thread().read(cx);
if !thread_state.tool_use_limit_reached() {
return;
}
let model = thread_state.configured_model().map(|cm| cm.model.clone());
if let Some(model) = model {
- self.thread.update(cx, |active_thread, cx| {
+ thread.update(cx, |active_thread, cx| {
active_thread.thread().update(cx, |thread, cx| {
thread.insert_invisible_continue_message(cx);
thread.advance_prompt_id();
@@ -1308,7 +1279,11 @@ impl AgentPanel {
_window: &mut Window,
cx: &mut Context<Self>,
) {
- self.thread.update(cx, |active_thread, cx| {
+ let ActiveView::Thread { thread, .. } = &self.active_view else {
+ return;
+ };
+
+ thread.update(cx, |active_thread, cx| {
active_thread.thread().update(cx, |thread, _cx| {
let current_mode = thread.completion_mode();
@@ -1353,13 +1328,12 @@ impl AgentPanel {
match &self.active_view {
ActiveView::Thread { thread, .. } => {
- if let Some(thread) = thread.upgrade() {
- if thread.read(cx).is_empty() {
- let id = thread.read(cx).id().clone();
- self.history_store.update(cx, |store, cx| {
- store.remove_recently_opened_thread(id, cx);
- });
- }
+ let thread = thread.read(cx);
+ if thread.is_empty() {
+ let id = thread.thread().read(cx).id().clone();
+ self.history_store.update(cx, |store, cx| {
+ store.remove_recently_opened_thread(id, cx);
+ });
}
}
_ => {}
@@ -1367,10 +1341,8 @@ impl AgentPanel {
match &new_view {
ActiveView::Thread { thread, .. } => self.history_store.update(cx, |store, cx| {
- if let Some(thread) = thread.upgrade() {
- let id = thread.read(cx).id().clone();
- store.push_recently_opened_entry(HistoryEntryId::Thread(id), cx);
- }
+ let id = thread.read(cx).thread().read(cx).id().clone();
+ store.push_recently_opened_entry(HistoryEntryId::Thread(id), cx);
}),
ActiveView::TextThread { context_editor, .. } => {
self.history_store.update(cx, |store, cx| {
@@ -1464,7 +1436,7 @@ impl AgentPanel {
impl Focusable for AgentPanel {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match &self.active_view {
- ActiveView::Thread { .. } => self.message_editor.focus_handle(cx),
+ ActiveView::Thread { message_editor, .. } => message_editor.focus_handle(cx),
ActiveView::History => self.history.focus_handle(cx),
ActiveView::TextThread { context_editor, .. } => context_editor.focus_handle(cx),
ActiveView::Configuration => {
@@ -1573,14 +1545,17 @@ impl AgentPanel {
let content = match &self.active_view {
ActiveView::Thread {
+ thread: active_thread,
change_title_editor,
..
} => {
- let active_thread = self.thread.read(cx);
- let state = if active_thread.is_empty() {
- &ThreadSummary::Pending
- } else {
- active_thread.summary(cx)
+ let state = {
+ let active_thread = active_thread.read(cx);
+ if active_thread.is_empty() {
+ &ThreadSummary::Pending
+ } else {
+ active_thread.summary(cx)
+ }
};
match state {
@@ -1600,7 +1575,7 @@ impl AgentPanel {
.child(
ui::IconButton::new("retry-summary-generation", IconName::RotateCcw)
.on_click({
- let active_thread = self.thread.clone();
+ let active_thread = active_thread.clone();
move |_, _window, cx| {
active_thread.update(cx, |thread, cx| {
thread.regenerate_summary(cx);
@@ -1681,22 +1656,11 @@ impl AgentPanel {
}
fn render_toolbar(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
- let active_thread = self.thread.read(cx);
let user_store = self.user_store.read(cx);
- let thread = active_thread.thread().read(cx);
- let thread_id = thread.id().clone();
- let is_empty = active_thread.is_empty();
- let editor_empty = self.message_editor.read(cx).is_editor_fully_empty(cx);
let usage = user_store.model_request_usage();
let account_url = zed_urls::account_url(cx);
- let show_token_count = match &self.active_view {
- ActiveView::Thread { .. } => !is_empty || !editor_empty,
- ActiveView::TextThread { .. } => true,
- _ => false,
- };
-
let focus_handle = self.focus_handle(cx);
let go_back_button = div().child(
@@ -1761,6 +1725,11 @@ impl AgentPanel {
"Zoom In"
};
+ let active_thread = match &self.active_view {
+ ActiveView::Thread { thread, .. } => Some(thread.read(cx).thread().clone()),
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => None,
+ };
+
let agent_extra_menu = PopoverMenu::new("agent-options-menu")
.trigger_with_tooltip(
IconButton::new("agent-options-menu", IconName::Ellipsis)
@@ -1781,17 +1750,23 @@ impl AgentPanel {
.anchor(Corner::TopRight)
.with_handle(self.assistant_dropdown_menu_handle.clone())
.menu(move |window, cx| {
- Some(ContextMenu::build(window, cx, |mut menu, _window, _cx| {
+ let active_thread = active_thread.clone();
+ Some(ContextMenu::build(window, cx, |mut menu, _window, cx| {
menu = menu
.action("New Thread", NewThread::default().boxed_clone())
.action("New Text Thread", NewTextThread.boxed_clone())
- .when(!is_empty, |menu| {
- menu.action(
- "New From Summary",
- Box::new(NewThread {
- from_thread_id: Some(thread_id.clone()),
- }),
- )
+ .when_some(active_thread, |this, active_thread| {
+ let thread = active_thread.read(cx);
+ if !thread.is_empty() {
+ this.action(
+ "New From Summary",
+ Box::new(NewThread {
+ from_thread_id: Some(thread.id().clone()),
+ }),
+ )
+ } else {
+ this
+ }
})
.separator();
@@ -1878,9 +1853,7 @@ impl AgentPanel {
h_flex()
.h_full()
.gap_2()
- .when(show_token_count, |parent| {
- parent.children(self.render_token_count(&thread, cx))
- })
+ .children(self.render_token_count(cx))
.child(
h_flex()
.h_full()
@@ -1913,26 +1886,41 @@ impl AgentPanel {
)
}
- fn render_token_count(&self, thread: &Thread, cx: &App) -> Option<AnyElement> {
- let is_generating = thread.is_generating();
- let message_editor = self.message_editor.read(cx);
+ fn render_token_count(&self, cx: &App) -> Option<AnyElement> {
+ let (active_thread, message_editor) = match &self.active_view {
+ ActiveView::Thread {
+ thread,
+ message_editor,
+ ..
+ } => (thread.read(cx), message_editor.read(cx)),
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => {
+ return None;
+ }
+ };
+
+ let editor_empty = message_editor.is_editor_fully_empty(cx);
+ if active_thread.is_empty() && editor_empty {
+ return None;
+ }
+
+ let thread = active_thread.thread().read(cx);
+ let is_generating = thread.is_generating();
let conversation_token_usage = thread.total_token_usage()?;
- let (total_token_usage, is_estimating) = if let Some((editing_message_id, unsent_tokens)) =
- self.thread.read(cx).editing_message_id()
- {
- let combined = thread
- .token_usage_up_to_message(editing_message_id)
- .add(unsent_tokens);
+ let (total_token_usage, is_estimating) =
+ if let Some((editing_message_id, unsent_tokens)) = active_thread.editing_message_id() {
+ let combined = thread
+ .token_usage_up_to_message(editing_message_id)
+ .add(unsent_tokens);
- (combined, unsent_tokens > 0)
- } else {
- let unsent_tokens = message_editor.last_estimated_token_count().unwrap_or(0);
- let combined = conversation_token_usage.add(unsent_tokens);
+ (combined, unsent_tokens > 0)
+ } else {
+ let unsent_tokens = message_editor.last_estimated_token_count().unwrap_or(0);
+ let combined = conversation_token_usage.add(unsent_tokens);
- (combined, unsent_tokens > 0)
- };
+ (combined, unsent_tokens > 0)
+ };
let is_waiting_to_update_token_count = message_editor.is_waiting_to_update_token_count();
@@ -2030,24 +2018,27 @@ impl AgentPanel {
}
fn should_render_upsell(&self, cx: &mut Context<Self>) -> bool {
- if !matches!(self.active_view, ActiveView::Thread { .. }) {
- return false;
- }
+ match &self.active_view {
+ ActiveView::Thread { thread, .. } => {
+ let is_using_zed_provider = thread
+ .read(cx)
+ .thread()
+ .read(cx)
+ .configured_model()
+ .map_or(false, |model| {
+ model.provider.id().0 == ZED_CLOUD_PROVIDER_ID
+ });
- if self.hide_upsell || Upsell::dismissed() {
- return false;
- }
+ if !is_using_zed_provider {
+ return false;
+ }
+ }
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => {
+ return false;
+ }
+ };
- let is_using_zed_provider = self
- .thread
- .read(cx)
- .thread()
- .read(cx)
- .configured_model()
- .map_or(false, |model| {
- model.provider.id().0 == ZED_CLOUD_PROVIDER_ID
- });
- if !is_using_zed_provider {
+ if self.hide_upsell || Upsell::dismissed() {
return false;
}
@@ -2352,20 +2343,6 @@ impl AgentPanel {
)
}
- fn render_active_thread_or_empty_state(
- &self,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> AnyElement {
- if self.thread.read(cx).is_empty() {
- return self
- .render_thread_empty_state(window, cx)
- .into_any_element();
- }
-
- self.thread.clone().into_any_element()
- }
-
fn render_thread_empty_state(
&self,
window: &mut Window,
@@ -2638,23 +2615,21 @@ impl AgentPanel {
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<AnyElement> {
- let tool_use_limit_reached = self
- .thread
- .read(cx)
- .thread()
- .read(cx)
- .tool_use_limit_reached();
+ let active_thread = match &self.active_view {
+ ActiveView::Thread { thread, .. } => thread,
+ ActiveView::TextThread { .. } | ActiveView::History | ActiveView::Configuration => {
+ return None;
+ }
+ };
+
+ let thread = active_thread.read(cx).thread().read(cx);
+
+ let tool_use_limit_reached = thread.tool_use_limit_reached();
if !tool_use_limit_reached {
return None;
}
- let model = self
- .thread
- .read(cx)
- .thread()
- .read(cx)
- .configured_model()?
- .model;
+ let model = thread.configured_model()?.model;
let focus_handle = self.focus_handle(cx);
@@ -2698,14 +2673,17 @@ impl AgentPanel {
.map(|kb| kb.size(rems_from_px(10.))),
)
.tooltip(Tooltip::text("Enable Burn Mode for unlimited tool use."))
- .on_click(cx.listener(|this, _, window, cx| {
- this.thread.update(cx, |active_thread, cx| {
- active_thread.thread().update(cx, |thread, _cx| {
- thread.set_completion_mode(CompletionMode::Burn);
+ .on_click({
+ let active_thread = active_thread.clone();
+ cx.listener(move |this, _, window, cx| {
+ active_thread.update(cx, |active_thread, cx| {
+ active_thread.thread().update(cx, |thread, _cx| {
+ thread.set_completion_mode(CompletionMode::Burn);
+ });
});
- });
- this.continue_conversation(window, cx);
- })),
+ this.continue_conversation(window, cx);
+ })
+ }),
)
}),
);
@@ -2713,33 +2691,11 @@ impl AgentPanel {
Some(div().px_2().pb_2().child(banner).into_any_element())
}
- fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
- let last_error = self.thread.read(cx).last_error()?;
-
- Some(
- div()
- .absolute()
- .right_3()
- .bottom_12()
- .max_w_96()
- .py_2()
- .px_3()
- .elevation_2(cx)
- .occlude()
- .child(match last_error {
- ThreadError::PaymentRequired => self.render_payment_required_error(cx),
- ThreadError::ModelRequestLimitReached { plan } => {
- self.render_model_request_limit_reached_error(plan, cx)
- }
- ThreadError::Message { header, message } => {
- self.render_error_message(header, message, cx)
- }
- })
- .into_any(),
- )
- }
-
- fn render_payment_required_error(&self, cx: &mut Context<Self>) -> AnyElement {
+ fn render_payment_required_error(
+ &self,
+ thread: &Entity<ActiveThread>,
+ cx: &mut Context<Self>,
+ ) -> AnyElement {
const ERROR_MESSAGE: &str = "Free tier exceeded. Subscribe and add payment to continue using Zed LLMs. You'll be billed at cost for tokens used.";
v_flex()
@@ -2764,25 +2720,27 @@ impl AgentPanel {
.mt_1()
.gap_1()
.child(self.create_copy_button(ERROR_MESSAGE))
- .child(Button::new("subscribe", "Subscribe").on_click(cx.listener(
- |this, _, _, cx| {
- this.thread.update(cx, |this, _cx| {
+ .child(Button::new("subscribe", "Subscribe").on_click(cx.listener({
+ let thread = thread.clone();
+ move |_, _, _, cx| {
+ thread.update(cx, |this, _cx| {
this.clear_last_error();
});
cx.open_url(&zed_urls::account_url(cx));
cx.notify();
- },
- )))
- .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
- |this, _, _, cx| {
- this.thread.update(cx, |this, _cx| {
+ }
+ })))
+ .child(Button::new("dismiss", "Dismiss").on_click(cx.listener({
+ let thread = thread.clone();
+ move |_, _, _, cx| {
+ thread.update(cx, |this, _cx| {
this.clear_last_error();
});
cx.notify();
- },
- ))),
+ }
+ }))),
)
.into_any()
}