Add the `MentionCrease` component

Danilo Leal created

Change summary

crates/agent_ui/src/ui.rs                |  2 
crates/agent_ui/src/ui/mention_crease.rs | 99 ++++++++++++++++++++++++++
2 files changed, 101 insertions(+)

Detailed changes

crates/agent_ui/src/ui.rs 🔗

@@ -4,6 +4,7 @@ mod burn_mode_tooltip;
 mod claude_code_onboarding_modal;
 mod end_trial_upsell;
 mod hold_for_default;
+mod mention_crease;
 mod model_selector_components;
 mod onboarding_modal;
 mod usage_callout;
@@ -14,6 +15,7 @@ pub use burn_mode_tooltip::*;
 pub use claude_code_onboarding_modal::*;
 pub use end_trial_upsell::*;
 pub use hold_for_default::*;
+pub use mention_crease::*;
 pub use model_selector_components::*;
 pub use onboarding_modal::*;
 pub use usage_callout::*;

crates/agent_ui/src/ui/mention_crease.rs 🔗

@@ -0,0 +1,99 @@
+use std::time::Duration;
+
+use gpui::{Animation, AnimationExt, AnyView, IntoElement, Window, pulsating_between};
+use settings::Settings;
+use theme::ThemeSettings;
+use ui::{ButtonLike, TintColor, prelude::*};
+
+#[derive(IntoElement)]
+pub struct MentionCrease {
+    id: ElementId,
+    icon: SharedString,
+    label: SharedString,
+    is_toggled: bool,
+    is_loading: bool,
+    image_preview: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
+}
+
+impl MentionCrease {
+    pub fn new(
+        id: impl Into<ElementId>,
+        icon: impl Into<SharedString>,
+        label: impl Into<SharedString>,
+    ) -> Self {
+        Self {
+            id: id.into(),
+            icon: icon.into(),
+            label: label.into(),
+            is_toggled: false,
+            is_loading: false,
+            image_preview: None,
+        }
+    }
+
+    pub fn is_toggled(mut self, is_toggled: bool) -> Self {
+        self.is_toggled = is_toggled;
+        self
+    }
+
+    pub fn is_loading(mut self, is_loading: bool) -> Self {
+        self.is_loading = is_loading;
+        self
+    }
+
+    pub fn image_preview(
+        mut self,
+        builder: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
+    ) -> Self {
+        self.image_preview = Some(Box::new(builder));
+        self
+    }
+}
+
+impl RenderOnce for MentionCrease {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
+        let settings = ThemeSettings::get_global(cx);
+        let font_size = settings.agent_buffer_font_size(cx);
+        let buffer_font = settings.buffer_font.clone();
+
+        let button_height = DefiniteLength::Absolute(AbsoluteLength::Pixels(
+            px(window.line_height().into()) - px(2.),
+        ));
+
+        ButtonLike::new(self.id)
+            .style(ButtonStyle::Outlined)
+            .size(ButtonSize::Compact)
+            .height(button_height)
+            .selected_style(ButtonStyle::Tinted(TintColor::Accent))
+            .toggle_state(self.is_toggled)
+            .when_some(self.image_preview, |this, image_preview| {
+                this.hoverable_tooltip(image_preview)
+            })
+            .child(
+                h_flex()
+                    .gap_1()
+                    .font(buffer_font)
+                    .text_size(font_size)
+                    .child(
+                        Icon::from_path(self.icon.clone())
+                            .size(IconSize::XSmall)
+                            .color(Color::Muted),
+                    )
+                    .child(self.label.clone())
+                    .map(|this| {
+                        if self.is_loading {
+                            this.with_animation(
+                                "loading-context-crease",
+                                Animation::new(Duration::from_secs(2))
+                                    .repeat()
+                                    .with_easing(pulsating_between(0.4, 0.8)),
+                                |label, delta| label.opacity(delta),
+                            )
+                            .into_any()
+                        } else {
+                            this.into_any()
+                        }
+                    }),
+            )
+    }
+}