use gpui::{Div, SharedString, div, prelude::*, px};
use theme::{TextStyle, Theme, ThemeExt, Typeset};
use crate::{stack, widgets::Layout};
const STEP_PAD_X: f32 = 10.0;
const STEP_PAD_Y: f32 = 6.0;
const STEP_OUTPUT_MAX: f32 = 256.0;
pub trait Status: ThemeExt {
fn step_row(
&self,
icon: &'static str,
title: impl Into<SharedString>,
detail: Option<SharedString>,
meta: Option<SharedString>,
failed: bool,
expanded: Option<bool>,
) -> Div {
let theme = self.theme();
stack::row()
.px(px(STEP_PAD_X))
.py(px(STEP_PAD_Y))
.cursor_pointer()
.child(
crate::icons::icon(icon)
.size(px(14.0))
.text_color(if failed {
theme.danger
} else {
theme.text_muted
}),
)
.child(
div()
.flex_none()
.text_style(TextStyle::Callout)
.text_color(theme.text)
.child(title.into()),
)
.when_some(detail, |row, detail| {
row.child(
div()
.min_w_0()
.truncate()
.text_style(TextStyle::Callout)
.text_color(theme.text_muted)
.child(detail),
)
})
.child(
div()
.ml_auto()
.flex_none()
.flex()
.flex_row()
.items_center()
.gap(px(6.0))
.when_some(meta, |cluster, meta| {
cluster.child(
div()
.font_family(theme.font_mono.clone())
.text_style(TextStyle::Callout)
.text_color(theme.text_muted)
.child(meta),
)
})
.when_some(expanded, |cluster, expanded| {
cluster.child(Layout::disclosure(theme, expanded))
}),
)
}
fn step_output(
&self,
id: impl Into<gpui::ElementId>,
text: impl Into<SharedString>,
) -> gpui::Stateful<Div> {
let theme = self.theme();
div()
.id(id)
.max_h(px(STEP_OUTPUT_MAX))
.overflow_y_scroll()
.border_t_1()
.border_color(theme.border)
.px(px(STEP_PAD_X))
.py(px(STEP_PAD_Y))
.font_family(theme.font_mono.clone())
.text_style(TextStyle::Callout)
.text_color(theme.text_muted)
.child(text.into())
}
fn error_strip(&self, message: impl Into<SharedString>) -> Div {
let theme = self.theme();
let red = theme.danger; let red_text = theme.danger_muted; div()
.mt(px(16.0))
.px(px(16.0))
.py(px(12.0))
.rounded(px(Theme::surface_radius()))
.border_1()
.border_color(red.opacity(0.2))
.bg(red.opacity(0.06))
.text_style(TextStyle::Callout)
.text_color(red_text.opacity(0.9))
.flex()
.flex_row()
.items_start()
.gap(px(Theme::SPACE))
.child(
div().flex_none().mt(px(2.0)).child(
crate::icons::icon(crate::icons::DANGER_TRIANGLE)
.size(px(16.0))
.text_color(red_text.opacity(0.9)),
),
)
.child(div().min_w_0().child(message.into()))
}
fn warning_strip(&self, message: impl Into<SharedString>) -> Div {
let theme = self.theme();
let amber = theme.warning; let amber_text = theme.warning_muted; div()
.mt(px(8.0))
.px(px(16.0))
.py(px(10.0))
.rounded(px(Theme::surface_radius()))
.border_1()
.border_color(amber.opacity(0.2))
.bg(amber.opacity(0.06))
.text_style(TextStyle::Callout)
.text_color(amber_text.opacity(0.9))
.flex()
.flex_row()
.items_start()
.gap(px(Theme::SPACE))
.child(
div().flex_none().mt(px(2.0)).child(
crate::icons::icon(crate::icons::DANGER_TRIANGLE)
.size(px(14.0))
.text_color(amber_text.opacity(0.9)),
),
)
.child(div().min_w_0().child(message.into()))
}
}
impl Status for Theme {}