use std::path::Path;
use gpui::{ClipboardItem, Div, ElementId, SharedString, Stateful, div, prelude::*, px};
use icons::Icon;
use theme::{TextStyle, Theme, ThemeExt, Typeset};
use crate::{stack, tooltip::Tooltip, 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: impl Into<Icon>,
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();
crate::scroll::contain_wheel(
crate::scroll::pane(id, crate::scroll::Axes::Vertical),
crate::scroll::Axes::Vertical,
)
.max_h(px(STEP_OUTPUT_MAX))
.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::glyph::TriangleAlert)
.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::glyph::TriangleAlert)
.size(px(14.0))
.text_color(amber_text.opacity(0.9)),
),
)
.child(div().min_w_0().child(message.into()))
}
fn status_bar(&self) -> Div {
let theme = self.theme();
div()
.h(px(30.0))
.flex_none()
.flex()
.items_center()
.gap(px(8.0))
.px(px(8.0))
.border_t_1()
.border_color(theme.border)
.text_style(TextStyle::Caption)
.text_color(theme.text_muted)
}
fn status_path(
&self,
id: impl Into<ElementId>,
path: &Path,
label: impl Into<SharedString>,
) -> Stateful<Div> {
let full = path.display().to_string();
let tip: SharedString = format!("{full}\nClick to copy path").into();
div()
.id(id)
.flex_1()
.min_w_0()
.truncate()
.cursor_pointer()
.tooltip(move |window, cx| Tooltip::text(tip.clone(), window, cx))
.on_click(move |_, _, cx| {
cx.write_to_clipboard(ClipboardItem::new_string(full.clone()))
})
.child(label.into())
}
}
impl Status for Theme {}