1use crate::{DiffStat, Divider, prelude::*};
2use gpui::{Animation, AnimationExt, pulsating_between};
3use std::time::Duration;
4
5#[derive(IntoElement)]
6pub struct ParallelAgentsIllustration;
7
8impl ParallelAgentsIllustration {
9 pub fn new() -> Self {
10 Self
11 }
12}
13
14impl RenderOnce for ParallelAgentsIllustration {
15 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
16 let icon_container = || h_flex().size_4().flex_shrink_0().justify_center();
17
18 let title_bar = |id: &'static str, width: DefiniteLength, duration_ms: u64| {
19 div()
20 .h_2()
21 .w(width)
22 .rounded_full()
23 .debug_bg_blue()
24 .bg(cx.theme().colors().element_selected)
25 .with_animation(
26 id,
27 Animation::new(Duration::from_millis(duration_ms))
28 .repeat()
29 .with_easing(pulsating_between(0.4, 0.8)),
30 |label, delta| label.opacity(delta),
31 )
32 };
33
34 let time =
35 |time: SharedString| Label::new(time).size(LabelSize::XSmall).color(Color::Muted);
36
37 let worktree = |worktree: SharedString| {
38 h_flex()
39 .gap_1()
40 .child(
41 Icon::new(IconName::GitWorktree)
42 .color(Color::Muted)
43 .size(IconSize::XSmall),
44 )
45 .child(
46 Label::new(worktree)
47 .size(LabelSize::XSmall)
48 .color(Color::Muted),
49 )
50 };
51
52 let dot_separator = || {
53 Label::new("•")
54 .size(LabelSize::Small)
55 .color(Color::Muted)
56 .alpha(0.5)
57 };
58
59 let agent = |id: &'static str,
60 icon: IconName,
61 width: DefiniteLength,
62 duration_ms: u64,
63 data: Vec<AnyElement>| {
64 v_flex()
65 .p_2()
66 .child(
67 h_flex()
68 .w_full()
69 .gap_2()
70 .child(
71 icon_container()
72 .child(Icon::new(icon).size(IconSize::Small).color(Color::Muted)),
73 )
74 .child(title_bar(id, width, duration_ms)),
75 )
76 .child(
77 h_flex()
78 .opacity(0.8)
79 .w_full()
80 .gap_2()
81 .child(icon_container())
82 .children(data),
83 )
84 };
85
86 let agents = v_flex()
87 .absolute()
88 .w(rems_from_px(380.))
89 .top_8()
90 .rounded_t_sm()
91 .border_1()
92 .border_color(cx.theme().colors().border.opacity(0.5))
93 .bg(cx.theme().colors().elevated_surface_background)
94 .shadow_md()
95 .child(agent(
96 "zed-agent-bar",
97 IconName::ZedAgent,
98 relative(0.7),
99 1800,
100 vec![
101 worktree("happy-tree".into()).into_any_element(),
102 dot_separator().into_any_element(),
103 DiffStat::new("ds", 23, 13)
104 .label_size(LabelSize::XSmall)
105 .into_any_element(),
106 dot_separator().into_any_element(),
107 time("2m".into()).into_any_element(),
108 ],
109 ))
110 .child(Divider::horizontal())
111 .child(agent(
112 "claude-bar",
113 IconName::AiClaude,
114 relative(0.85),
115 2400,
116 vec![
117 DiffStat::new("ds", 120, 84)
118 .label_size(LabelSize::XSmall)
119 .into_any_element(),
120 dot_separator().into_any_element(),
121 time("16m".into()).into_any_element(),
122 ],
123 ))
124 .child(Divider::horizontal())
125 .child(agent(
126 "openai-bar",
127 IconName::AiOpenAi,
128 relative(0.4),
129 3100,
130 vec![
131 worktree("silent-forest".into()).into_any_element(),
132 dot_separator().into_any_element(),
133 time("37m".into()).into_any_element(),
134 ],
135 ))
136 .child(Divider::horizontal());
137
138 h_flex()
139 .relative()
140 .h(rems_from_px(180.))
141 .bg(cx.theme().colors().editor_background)
142 .justify_center()
143 .items_end()
144 .rounded_t_md()
145 .overflow_hidden()
146 .bg(gpui::black().opacity(0.2))
147 .child(agents)
148 }
149}