1use gpui::{AnyElement, EntityId, prelude::*};
2use ui::{Tooltip, prelude::*};
3
4#[derive(IntoElement)]
5pub struct ToolOutputPreview<F>
6where
7 F: Fn(bool, &mut Window, &mut App) + 'static,
8{
9 content: AnyElement,
10 entity_id: EntityId,
11 full_height: bool,
12 total_lines: usize,
13 collapsed_fade: bool,
14 on_toggle: Option<F>,
15}
16
17pub const COLLAPSED_LINES: usize = 10;
18
19impl<F> ToolOutputPreview<F>
20where
21 F: Fn(bool, &mut Window, &mut App) + 'static,
22{
23 pub fn new(content: AnyElement, entity_id: EntityId) -> Self {
24 Self {
25 content,
26 entity_id,
27 full_height: true,
28 total_lines: 0,
29 collapsed_fade: false,
30 on_toggle: None,
31 }
32 }
33
34 pub fn with_total_lines(mut self, total_lines: usize) -> Self {
35 self.total_lines = total_lines;
36 self
37 }
38
39 pub fn toggle_state(mut self, full_height: bool) -> Self {
40 self.full_height = full_height;
41 self
42 }
43
44 pub fn with_collapsed_fade(mut self) -> Self {
45 self.collapsed_fade = true;
46 self
47 }
48
49 pub fn on_toggle(mut self, listener: F) -> Self {
50 self.on_toggle = Some(listener);
51 self
52 }
53}
54
55impl<F> RenderOnce for ToolOutputPreview<F>
56where
57 F: Fn(bool, &mut Window, &mut App) + 'static,
58{
59 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
60 if self.total_lines <= COLLAPSED_LINES {
61 return self.content;
62 }
63 let border_color = cx.theme().colors().border.opacity(0.6);
64
65 let (icon, tooltip_label) = if self.full_height {
66 (IconName::ChevronUp, "Collapse")
67 } else {
68 (IconName::ChevronDown, "Expand")
69 };
70
71 let gradient_overlay =
72 if self.collapsed_fade && !self.full_height {
73 Some(div().absolute().bottom_5().left_0().w_full().h_2_5().bg(
74 gpui::linear_gradient(
75 0.,
76 gpui::linear_color_stop(cx.theme().colors().editor_background, 0.),
77 gpui::linear_color_stop(
78 cx.theme().colors().editor_background.opacity(0.),
79 1.,
80 ),
81 ),
82 ))
83 } else {
84 None
85 };
86
87 v_flex()
88 .relative()
89 .child(self.content)
90 .children(gradient_overlay)
91 .child(
92 h_flex()
93 .id(("expand-button", self.entity_id))
94 .flex_none()
95 .cursor_pointer()
96 .h_5()
97 .justify_center()
98 .border_t_1()
99 .rounded_b_md()
100 .border_color(border_color)
101 .bg(cx.theme().colors().editor_background)
102 .hover(|style| style.bg(cx.theme().colors().element_hover.opacity(0.1)))
103 .child(Icon::new(icon).size(IconSize::Small).color(Color::Muted))
104 .tooltip(Tooltip::text(tooltip_label))
105 .when_some(self.on_toggle, |this, on_toggle| {
106 this.on_click({
107 move |_, window, cx| {
108 on_toggle(!self.full_height, window, cx);
109 }
110 })
111 }),
112 )
113 .into_any()
114 }
115}