Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T06:32:53.250Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

banner.rs

192 lines · 6.3 KB · rust
1use crate::prelude::*;
2use gpui::{AnyElement, IntoElement, ParentElement, Styled};
3
4/// Banners provide informative and brief messages without interrupting the user.
5/// This component offers four severity levels that can be used depending on the message.
6///
7/// # Usage Example
8///
9/// ```
10/// use ui::prelude::*;
11/// use ui::{Banner, Button, Icon, IconName, IconSize, Label, Severity};
12///
13/// Banner::new()
14///     .severity(Severity::Success)
15///     .children([Label::new("This is a success message")])
16///     .action_slot(
17///         Button::new("learn-more", "Learn More")
18///             .end_icon(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
19///     );
20/// ```
21#[derive(IntoElement, RegisterComponent)]
22pub struct Banner {
23    severity: Severity,
24    children: Vec<AnyElement>,
25    action_slot: Option<AnyElement>,
26    wrap_content: bool,
27}
28
29impl Banner {
30    /// Creates a new `Banner` component with default styling.
31    pub fn new() -> Self {
32        Self {
33            severity: Severity::Info,
34            children: Vec::new(),
35            action_slot: None,
36            wrap_content: false,
37        }
38    }
39
40    /// Sets the severity of the banner.
41    pub fn severity(mut self, severity: Severity) -> Self {
42        self.severity = severity;
43        self
44    }
45
46    /// A slot for actions, such as CTA or dismissal buttons.
47    pub fn action_slot(mut self, element: impl IntoElement) -> Self {
48        self.action_slot = Some(element.into_any_element());
49        self
50    }
51
52    /// Sets whether the banner content should wrap.
53    pub fn wrap_content(mut self, wrap: bool) -> Self {
54        self.wrap_content = wrap;
55        self
56    }
57}
58
59impl ParentElement for Banner {
60    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
61        self.children.extend(elements)
62    }
63}
64
65impl RenderOnce for Banner {
66    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
67        let banner = h_flex()
68            .min_w_0()
69            .py_0p5()
70            .gap_1p5()
71            .when(self.wrap_content, |this| this.flex_wrap())
72            .justify_between()
73            .rounded_sm()
74            .border_1();
75
76        let (icon, icon_color, bg_color, border_color) = match self.severity {
77            Severity::Info => (
78                IconName::Info,
79                Color::Muted,
80                cx.theme().status().info_background.opacity(0.5),
81                cx.theme().colors().border.opacity(0.5),
82            ),
83            Severity::Success => (
84                IconName::Check,
85                Color::Success,
86                cx.theme().status().success.opacity(0.1),
87                cx.theme().status().success.opacity(0.2),
88            ),
89            Severity::Warning => (
90                IconName::Warning,
91                Color::Warning,
92                cx.theme().status().warning_background.opacity(0.5),
93                cx.theme().status().warning_border.opacity(0.4),
94            ),
95            Severity::Error => (
96                IconName::XCircle,
97                Color::Error,
98                cx.theme().status().error.opacity(0.1),
99                cx.theme().status().error.opacity(0.2),
100            ),
101        };
102
103        let mut banner = banner.bg(bg_color).border_color(border_color);
104
105        let icon_and_child = h_flex()
106            .items_start()
107            .min_w_0()
108            .flex_1()
109            .gap_1p5()
110            .child(
111                h_flex()
112                    .h(window.line_height())
113                    .flex_shrink_0()
114                    .child(Icon::new(icon).size(IconSize::XSmall).color(icon_color)),
115            )
116            .child(div().min_w_0().flex_1().children(self.children));
117
118        if let Some(action_slot) = self.action_slot {
119            banner = banner
120                .pl_2()
121                .pr_1()
122                .child(icon_and_child)
123                .child(action_slot);
124        } else {
125            banner = banner.px_2().child(icon_and_child);
126        }
127
128        banner
129    }
130}
131
132impl Component for Banner {
133    fn scope() -> ComponentScope {
134        ComponentScope::DataDisplay
135    }
136
137    fn description() -> &'static str {
138        "A non-blocking, severity-aware message strip used to surface informative, \
139        success, warning, or error messages without interrupting the user."
140    }
141
142    fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
143        let severity_examples = vec![
144            single_example(
145                "Default",
146                Banner::new()
147                    .child(Label::new("This is a default banner with no customization"))
148                    .into_any_element(),
149            ),
150            single_example(
151                "Info",
152                Banner::new()
153                    .severity(Severity::Info)
154                    .child(Label::new("This is an informational message"))
155                    .action_slot(
156                        Button::new("learn-more", "Learn More")
157                            .end_icon(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
158                    )
159                    .into_any_element(),
160            ),
161            single_example(
162                "Success",
163                Banner::new()
164                    .severity(Severity::Success)
165                    .child(Label::new("Operation completed successfully"))
166                    .action_slot(Button::new("dismiss", "Dismiss"))
167                    .into_any_element(),
168            ),
169            single_example(
170                "Warning",
171                Banner::new()
172                    .severity(Severity::Warning)
173                    .child(Label::new("Your settings file uses deprecated settings"))
174                    .action_slot(Button::new("update", "Update Settings"))
175                    .into_any_element(),
176            ),
177            single_example(
178                "Error",
179                Banner::new()
180                    .severity(Severity::Error)
181                    .child(Label::new("Connection error: unable to connect to server"))
182                    .action_slot(Button::new("reconnect", "Retry"))
183                    .into_any_element(),
184            ),
185        ];
186
187        example_group(severity_examples)
188            .vertical()
189            .into_any_element()
190    }
191}
192
Served at tenant.openagents/omega Member data and write actions are omitted.