ui: Refactor the Callout component (#32684)

What motivated me to refactor this component was the fact that I wanted
a new variant to allow having _two CTAs_ instead of just one. This
variant should work with either a single or multiline description. But,
given we were using a `Callout::single_line` and `Callout::multi_line`
API, I'd then need to have both `Callout::single_line_one_button` and
`Callout::single_line_two_buttons` type of variants, which just points
to a combinatorial problem.

With this refactor, the Callout now follows the same structure of the
Banner component, where it's all `Callout::new` and every method is
passed as if they were props in a React component, allowing for a more
flexible design where you can customize button styles. Also made it
slightly more robust for wrapping and removed the top border as that
should be defined by the place it is being used in.

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-06-13 10:03:32 -03:00 committed by GitHub
parent aa1cb9c1e1
commit 29f3e62850
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 212 additions and 129 deletions

View file

@ -39,7 +39,7 @@ use proto::Plan;
use settings::Settings;
use std::time::Duration;
use theme::ThemeSettings;
use ui::{Disclosure, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*};
use ui::{Callout, Disclosure, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*};
use util::{ResultExt as _, maybe};
use workspace::{CollaboratorId, Workspace};
use zed_llm_client::CompletionIntent;
@ -1175,6 +1175,7 @@ impl MessageEditor {
.map_or(false, |model| {
model.provider.id().0 == ZED_CLOUD_PROVIDER_ID
});
if !is_using_zed_provider {
return None;
}
@ -1229,14 +1230,6 @@ impl MessageEditor {
token_usage_ratio: TokenUsageRatio,
cx: &mut Context<Self>,
) -> Option<Div> {
let title = if token_usage_ratio == TokenUsageRatio::Exceeded {
"Thread reached the token limit"
} else {
"Thread reaching the token limit soon"
};
let message = "Start a new thread from a summary to continue the conversation.";
let icon = if token_usage_ratio == TokenUsageRatio::Exceeded {
Icon::new(IconName::X)
.color(Color::Error)
@ -1247,19 +1240,36 @@ impl MessageEditor {
.size(IconSize::XSmall)
};
let title = if token_usage_ratio == TokenUsageRatio::Exceeded {
"Thread reached the token limit"
} else {
"Thread reaching the token limit soon"
};
Some(
div()
.child(ui::Callout::multi_line(
title,
message,
icon,
"Start New Thread",
Box::new(cx.listener(|this, _, window, cx| {
let from_thread_id = Some(this.thread.read(cx).id().clone());
window.dispatch_action(Box::new(NewThread { from_thread_id }), cx);
})),
))
.line_height(line_height),
.border_t_1()
.border_color(cx.theme().colors().border)
.child(
Callout::new()
.line_height(line_height)
.icon(icon)
.title(title)
.description(
"Start a new thread from a summary to continue the conversation.",
)
.primary_action(
Button::new("start-new-thread", "Start New Thread")
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
let from_thread_id = Some(this.thread.read(cx).id().clone());
window.dispatch_action(
Box::new(NewThread { from_thread_id }),
cx,
);
})),
),
),
)
}

View file

@ -2,7 +2,7 @@ use client::zed_urls;
use component::{empty_example, example_group_with_title, single_example};
use gpui::{AnyElement, App, IntoElement, RenderOnce, Window};
use language_model::RequestUsage;
use ui::{Callout, Color, Icon, IconName, IconSize, prelude::*};
use ui::{Callout, prelude::*};
use zed_llm_client::{Plan, UsageLimit};
#[derive(IntoElement, RegisterComponent)]
@ -91,16 +91,23 @@ impl RenderOnce for UsageCallout {
.size(IconSize::XSmall)
};
Callout::multi_line(
title,
message,
icon,
button_text,
Box::new(move |_, _, cx| {
cx.open_url(&url);
}),
)
.into_any_element()
div()
.border_t_1()
.border_color(cx.theme().colors().border)
.child(
Callout::new()
.icon(icon)
.title(title)
.description(message)
.primary_action(
Button::new("upgrade", button_text)
.label_size(LabelSize::Small)
.on_click(move |_, _, cx| {
cx.open_url(&url);
}),
),
)
.into_any_element()
}
}
@ -189,10 +196,8 @@ impl Component for UsageCallout {
);
Some(
div()
v_flex()
.p_4()
.flex()
.flex_col()
.gap_4()
.child(free_examples)
.child(trial_examples)