260 lines · 8.3 KB · Rust
RawDownload
use crate::{    Chip, DecoratedIcon, DiffStat, IconDecoration, IconDecorationKind, SpinnerLabel, prelude::*,};use gpui::{ClickEvent, SharedString}; #[derive(IntoElement, RegisterComponent)]pub struct ThreadItem {    id: ElementId,    icon: IconName,    title: SharedString,    timestamp: SharedString,    running: bool,    generation_done: bool,    selected: bool,    added: Option<usize>,    removed: Option<usize>,    worktree: Option<SharedString>,    on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,} impl ThreadItem {    pub fn new(id: impl Into<ElementId>, title: impl Into<SharedString>) -> Self {        Self {            id: id.into(),            icon: IconName::ZedAgent,            title: title.into(),            timestamp: "".into(),            running: false,            generation_done: false,            selected: false,            added: None,            removed: None,            worktree: None,            on_click: None,        }    }     pub fn timestamp(mut self, timestamp: impl Into<SharedString>) -> Self {        self.timestamp = timestamp.into();        self    }     pub fn icon(mut self, icon: IconName) -> Self {        self.icon = icon;        self    }     pub fn running(mut self, running: bool) -> Self {        self.running = running;        self    }     pub fn generation_done(mut self, generation_done: bool) -> Self {        self.generation_done = generation_done;        self    }     pub fn selected(mut self, selected: bool) -> Self {        self.selected = selected;        self    }     pub fn added(mut self, added: usize) -> Self {        self.added = Some(added);        self    }     pub fn removed(mut self, removed: usize) -> Self {        self.removed = Some(removed);        self    }     pub fn worktree(mut self, worktree: impl Into<SharedString>) -> Self {        self.worktree = Some(worktree.into());        self    }     pub fn on_click(        mut self,        handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,    ) -> Self {        self.on_click = Some(Box::new(handler));        self    }} impl RenderOnce for ThreadItem {    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {        let icon_container = || h_flex().size_4().justify_center();        let agent_icon = Icon::new(self.icon)            .color(Color::Muted)            .size(IconSize::Small);         let icon = if self.generation_done {            DecoratedIcon::new(                agent_icon,                Some(                    IconDecoration::new(                        IconDecorationKind::Dot,                        cx.theme().colors().surface_background,                        cx,                    )                    .color(cx.theme().colors().text_accent)                    .position(gpui::Point {                        x: px(-2.),                        y: px(-2.),                    }),                ),            )            .into_any_element()        } else {            agent_icon.into_any_element()        };         let has_no_changes = self.added.is_none() && self.removed.is_none();         v_flex()            .id(self.id.clone())            .cursor_pointer()            .p_2()            .when(self.selected, |this| {                this.bg(cx.theme().colors().element_active)            })            .hover(|s| s.bg(cx.theme().colors().element_hover))            .child(                h_flex()                    .w_full()                    .gap_1p5()                    .child(icon)                    .child(Label::new(self.title).truncate())                    .when(self.running, |this| {                        this.child(icon_container().child(SpinnerLabel::new().color(Color::Accent)))                    }),            )            .child(                h_flex()                    .gap_1p5()                    .child(icon_container()) // Icon Spacing                    .when_some(self.worktree, |this, name| {                        this.child(Chip::new(name).label_size(LabelSize::XSmall))                    })                    .child(                        Label::new(self.timestamp)                            .size(LabelSize::Small)                            .color(Color::Muted),                    )                    .child(                        Label::new("•")                            .size(LabelSize::Small)                            .color(Color::Muted)                            .alpha(0.5),                    )                    .when(has_no_changes, |this| {                        this.child(                            Label::new("No Changes")                                .size(LabelSize::Small)                                .color(Color::Muted),                        )                    })                    .when(self.added.is_some() || self.removed.is_some(), |this| {                        this.child(DiffStat::new(                            self.id,                            self.added.unwrap_or(0),                            self.removed.unwrap_or(0),                        ))                    }),            )            .when_some(self.on_click, |this, on_click| this.on_click(on_click))    }} impl Component for ThreadItem {    fn scope() -> ComponentScope {        ComponentScope::Agent    }     fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {        let container = || {            v_flex()                .w_72()                .border_1()                .border_color(cx.theme().colors().border_variant)                .bg(cx.theme().colors().panel_background)        };         let thread_item_examples = vec![            single_example(                "Default",                container()                    .child(                        ThreadItem::new("ti-1", "Linking to the Agent Panel Depending on Settings")                            .icon(IconName::AiOpenAi)                            .timestamp("1:33 AM"),                    )                    .into_any_element(),            ),            single_example(                "Generation Done",                container()                    .child(                        ThreadItem::new("ti-2", "Refine thread view scrolling behavior")                            .timestamp("12:12 AM")                            .generation_done(true),                    )                    .into_any_element(),            ),            single_example(                "Running Agent",                container()                    .child(                        ThreadItem::new("ti-3", "Add line numbers option to FileEditBlock")                            .icon(IconName::AiClaude)                            .timestamp("7:30 PM")                            .running(true),                    )                    .into_any_element(),            ),            single_example(                "In Worktree",                container()                    .child(                        ThreadItem::new("ti-4", "Add line numbers option to FileEditBlock")                            .icon(IconName::AiClaude)                            .timestamp("7:37 PM")                            .worktree("link-agent-panel"),                    )                    .into_any_element(),            ),            single_example(                "With Changes",                container()                    .child(                        ThreadItem::new("ti-5", "Managing user and project settings interactions")                            .icon(IconName::AiClaude)                            .timestamp("7:37 PM")                            .added(10)                            .removed(3),                    )                    .into_any_element(),            ),            single_example(                "Selected Item",                container()                    .child(                        ThreadItem::new("ti-6", "Refine textarea interaction behavior")                            .icon(IconName::AiGemini)                            .timestamp("3:00 PM")                            .selected(true),                    )                    .into_any_element(),            ),        ];         Some(            example_group(thread_item_examples)                .vertical()                .into_any_element(),        )    }}