@@ -675,6 +675,8 @@ impl AcpThreadView {
})
});
+ this.message_editor.focus_handle(cx).focus(window);
+
cx.notify();
}
Err(err) => {
@@ -1009,6 +1011,10 @@ impl AcpThreadView {
}
}
+ pub fn is_loading(&self) -> bool {
+ matches!(self.thread_state, ThreadState::Loading { .. })
+ }
+
fn resume_chat(&mut self, cx: &mut Context<Self>) {
self.thread_error.take();
let Some(thread) = self.thread() else {
@@ -4196,8 +4202,10 @@ impl AcpThreadView {
.block_mouse_except_scroll();
let enable_editor = match self.thread_state {
- ThreadState::Loading { .. } | ThreadState::Ready { .. } => true,
- ThreadState::Unauthenticated { .. } | ThreadState::LoadError(..) => false,
+ ThreadState::Ready { .. } => true,
+ ThreadState::Loading { .. }
+ | ThreadState::Unauthenticated { .. }
+ | ThreadState::LoadError(..) => false,
};
v_flex()
@@ -5858,12 +5866,10 @@ fn placeholder_text(agent_name: &str, has_commands: bool) -> String {
impl Focusable for AcpThreadView {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match self.thread_state {
- ThreadState::Loading { .. } | ThreadState::Ready { .. } => {
- self.active_editor(cx).focus_handle(cx)
- }
- ThreadState::LoadError(_) | ThreadState::Unauthenticated { .. } => {
- self.focus_handle.clone()
- }
+ ThreadState::Ready { .. } => self.active_editor(cx).focus_handle(cx),
+ ThreadState::Loading { .. }
+ | ThreadState::LoadError(_)
+ | ThreadState::Unauthenticated { .. } => self.focus_handle.clone(),
}
}
}
@@ -1,7 +1,4 @@
-use std::ops::Range;
-use std::path::Path;
-use std::rc::Rc;
-use std::sync::Arc;
+use std::{ops::Range, path::Path, rc::Rc, sync::Arc, time::Duration};
use acp_thread::AcpThread;
use agent::{ContextServerRegistry, DbThreadMetadata, HistoryEntry, HistoryStore};
@@ -46,9 +43,9 @@ use extension::ExtensionEvents;
use extension_host::ExtensionStore;
use fs::Fs;
use gpui::{
- Action, AnyElement, App, AsyncWindowContext, Corner, DismissEvent, Entity, EventEmitter,
- ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, Subscription, Task, UpdateGlobal,
- WeakEntity, prelude::*,
+ Action, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, Corner, DismissEvent,
+ Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, Subscription,
+ Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,
};
use language::LanguageRegistry;
use language_model::{ConfigurationError, LanguageModelRegistry};
@@ -58,10 +55,9 @@ use rules_library::{RulesLibrary, open_rules_library};
use search::{BufferSearchBar, buffer_search};
use settings::{Settings, update_settings_file};
use theme::ThemeSettings;
-use ui::utils::WithRemSize;
use ui::{
Callout, ContextMenu, ContextMenuEntry, KeyBinding, PopoverMenu, PopoverMenuHandle,
- ProgressBar, Tab, Tooltip, prelude::*,
+ ProgressBar, Tab, Tooltip, prelude::*, utils::WithRemSize,
};
use util::ResultExt as _;
use workspace::{
@@ -2159,28 +2155,41 @@ impl AgentPanel {
let selected_agent_label = self.selected_agent.label();
+ let is_thread_loading = self
+ .active_thread_view()
+ .map(|thread| thread.read(cx).is_loading())
+ .unwrap_or(false);
+
let has_custom_icon = selected_agent_custom_icon.is_some();
+
let selected_agent = div()
.id("selected_agent_icon")
.when_some(selected_agent_custom_icon, |this, icon_path| {
- let label = selected_agent_label.clone();
this.px_1()
.child(Icon::from_external_svg(icon_path).color(Color::Muted))
- .tooltip(move |_window, cx| {
- Tooltip::with_meta(label.clone(), None, "Selected Agent", cx)
- })
})
.when(!has_custom_icon, |this| {
this.when_some(self.selected_agent.icon(), |this, icon| {
- let label = selected_agent_label.clone();
- this.px_1()
- .child(Icon::new(icon).color(Color::Muted))
- .tooltip(move |_window, cx| {
- Tooltip::with_meta(label.clone(), None, "Selected Agent", cx)
- })
+ this.px_1().child(Icon::new(icon).color(Color::Muted))
})
})
- .into_any_element();
+ .tooltip(move |_, cx| {
+ Tooltip::with_meta(selected_agent_label.clone(), None, "Selected Agent", cx)
+ });
+
+ let selected_agent = if is_thread_loading {
+ selected_agent
+ .with_animation(
+ "pulsating-icon",
+ Animation::new(Duration::from_secs(1))
+ .repeat()
+ .with_easing(pulsating_between(0.2, 0.6)),
+ |icon, delta| icon.opacity(delta),
+ )
+ .into_any_element()
+ } else {
+ selected_agent.into_any_element()
+ };
h_flex()
.id("agent-panel-toolbar")