@@ -34,9 +34,9 @@ use editor::{
use editor::{display_map::CreaseId, FoldPlaceholder};
use fs::Fs;
use gpui::{
- canvas, div, percentage, point, Action, Animation, AnimationExt, AnyElement, AnyView,
- AppContext, AsyncWindowContext, ClipboardItem, Context as _, DismissEvent, Empty, Entity,
- EntityId, EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement,
+ canvas, div, percentage, point, pulsating_between, Action, Animation, AnimationExt, AnyElement,
+ AnyView, AppContext, AsyncWindowContext, ClipboardItem, Context as _, DismissEvent, Empty,
+ Entity, EntityId, EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement,
IntoElement, Model, ParentElement, Pixels, ReadGlobal, Render, SharedString,
StatefulInteractiveElement, Styled, Subscription, Task, Transformation, UpdateGlobal, View,
ViewContext, VisualContext, WeakView, WindowContext,
@@ -2953,13 +2953,38 @@ impl ContextEditor {
let context = self.context.clone();
move |cx| {
let message_id = message.id;
+ let show_spinner = message.role == Role::Assistant
+ && message.status == MessageStatus::Pending;
+
+ let label = match message.role {
+ Role::User => {
+ Label::new("You").color(Color::Default).into_any_element()
+ }
+ Role::Assistant => {
+ let label = Label::new("Assistant").color(Color::Info);
+ if show_spinner {
+ label
+ .with_animation(
+ "pulsating-label",
+ Animation::new(Duration::from_secs(2))
+ .repeat()
+ .with_easing(pulsating_between(0.2, 1.0)),
+ |label, delta| label.alpha(delta),
+ )
+ .into_any_element()
+ } else {
+ label.into_any_element()
+ }
+ }
+
+ Role::System => Label::new("System")
+ .color(Color::Warning)
+ .into_any_element(),
+ };
+
let sender = ButtonLike::new("role")
.style(ButtonStyle::Filled)
- .child(match message.role {
- Role::User => Label::new("You").color(Color::Default),
- Role::Assistant => Label::new("Assistant").color(Color::Info),
- Role::System => Label::new("System").color(Color::Warning),
- })
+ .child(label)
.tooltip(|cx| {
Tooltip::with_meta(
"Toggle message role",
@@ -41,6 +41,9 @@ pub trait LabelCommon {
/// Sets the italic property of the label.
fn italic(self, italic: bool) -> Self;
+
+ /// Sets the alpha property of the label, overwriting the alpha value of the color.
+ fn alpha(self, alpha: f32) -> Self;
}
#[derive(IntoElement)]
@@ -53,6 +56,7 @@ pub struct LabelLike {
strikethrough: bool,
italic: bool,
children: SmallVec<[AnyElement; 2]>,
+ alpha: Option<f32>,
}
impl LabelLike {
@@ -66,6 +70,7 @@ impl LabelLike {
strikethrough: false,
italic: false,
children: SmallVec::new(),
+ alpha: None,
}
}
}
@@ -111,6 +116,11 @@ impl LabelCommon for LabelLike {
self.italic = italic;
self
}
+
+ fn alpha(mut self, alpha: f32) -> Self {
+ self.alpha = Some(alpha);
+ self
+ }
}
impl ParentElement for LabelLike {
@@ -123,6 +133,11 @@ impl RenderOnce for LabelLike {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx);
+ let mut color = self.color.color(cx);
+ if let Some(alpha) = self.alpha {
+ color.fade_out(1.0 - alpha);
+ }
+
self.base
.when(self.strikethrough, |this| {
this.relative().child(
@@ -144,7 +159,7 @@ impl RenderOnce for LabelLike {
this.line_height(relative(1.))
})
.when(self.italic, |this| this.italic())
- .text_color(self.color.color(cx))
+ .text_color(color)
.font_weight(self.weight.unwrap_or(settings.ui_font.weight))
.children(self.children)
}
@@ -1,5 +1,7 @@
+use std::time::Duration;
+
use crate::{prelude::*, HighlightedLabel, Label};
-use gpui::Render;
+use gpui::{pulsating_between, Animation, AnimationExt, Render};
use story::Story;
pub struct LabelStory;
@@ -23,5 +25,14 @@ impl Render for LabelStory {
.child(
HighlightedLabel::new("Hello, world!", vec![0, 1, 2, 7, 8, 12]).color(Color::Error),
)
+ .child(
+ Label::new("This text is pulsating").with_animation(
+ "pulsating-label",
+ Animation::new(Duration::from_secs(2))
+ .repeat()
+ .with_easing(pulsating_between(0.2, 1.0)),
+ |label, delta| label.alpha(delta),
+ ),
+ )
}
}