@@ -1,7 +1,7 @@
use crate::{Tooltip, prelude::*};
use gpui::{ClickEvent, IntoElement, ParentElement, SharedString};
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct ConfiguredApiCard {
label: SharedString,
button_label: Option<SharedString>,
@@ -52,6 +52,59 @@ impl ConfiguredApiCard {
}
}
+impl Component for ConfiguredApiCard {
+ fn scope() -> ComponentScope {
+ ComponentScope::Agent
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
+ let container = || {
+ v_flex()
+ .w_72()
+ .p_2()
+ .gap_2()
+ .border_1()
+ .border_color(cx.theme().colors().border_variant)
+ .bg(cx.theme().colors().panel_background)
+ };
+
+ let examples = vec![
+ single_example(
+ "Default",
+ container()
+ .child(ConfiguredApiCard::new("API key is configured"))
+ .into_any_element(),
+ ),
+ single_example(
+ "Custom Button Label",
+ container()
+ .child(
+ ConfiguredApiCard::new("OpenAI API key configured")
+ .button_label("Remove Key"),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "With Tooltip",
+ container()
+ .child(
+ ConfiguredApiCard::new("Anthropic API key configured")
+ .tooltip_label("Click to reset your API key"),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "Disabled",
+ container()
+ .child(ConfiguredApiCard::new("API key is configured").disabled(true))
+ .into_any_element(),
+ ),
+ ];
+
+ Some(example_group(examples).into_any_element())
+ }
+}
+
impl RenderOnce for ConfiguredApiCard {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let button_label = self.button_label.unwrap_or("Reset Key".into());
@@ -0,0 +1,177 @@
+use gpui::{AnyView, ClickEvent};
+use ui_macros::RegisterComponent;
+
+use crate::prelude::*;
+use crate::{IconButton, IconName, Tooltip};
+
+#[derive(IntoElement, RegisterComponent)]
+pub struct ThreadSidebarToggle {
+ sidebar_selected: bool,
+ thread_selected: bool,
+ flipped: bool,
+ sidebar_tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
+ thread_tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
+ on_sidebar_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
+ on_thread_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
+}
+
+impl ThreadSidebarToggle {
+ pub fn new() -> Self {
+ Self {
+ sidebar_selected: false,
+ thread_selected: false,
+ flipped: false,
+ sidebar_tooltip: None,
+ thread_tooltip: None,
+ on_sidebar_click: None,
+ on_thread_click: None,
+ }
+ }
+
+ pub fn sidebar_selected(mut self, selected: bool) -> Self {
+ self.sidebar_selected = selected;
+ self
+ }
+
+ pub fn thread_selected(mut self, selected: bool) -> Self {
+ self.thread_selected = selected;
+ self
+ }
+
+ pub fn flipped(mut self, flipped: bool) -> Self {
+ self.flipped = flipped;
+ self
+ }
+
+ pub fn sidebar_tooltip(
+ mut self,
+ tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
+ ) -> Self {
+ self.sidebar_tooltip = Some(Box::new(tooltip));
+ self
+ }
+
+ pub fn thread_tooltip(
+ mut self,
+ tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
+ ) -> Self {
+ self.thread_tooltip = Some(Box::new(tooltip));
+ self
+ }
+
+ pub fn on_sidebar_click(
+ mut self,
+ handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ ) -> Self {
+ self.on_sidebar_click = Some(Box::new(handler));
+ self
+ }
+
+ pub fn on_thread_click(
+ mut self,
+ handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ ) -> Self {
+ self.on_thread_click = Some(Box::new(handler));
+ self
+ }
+}
+
+impl RenderOnce for ThreadSidebarToggle {
+ fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
+ let sidebar_icon = match (self.sidebar_selected, self.flipped) {
+ (true, false) => IconName::ThreadsSidebarLeftOpen,
+ (false, false) => IconName::ThreadsSidebarLeftClosed,
+ (true, true) => IconName::ThreadsSidebarRightOpen,
+ (false, true) => IconName::ThreadsSidebarRightClosed,
+ };
+
+ h_flex()
+ .min_w_0()
+ .rounded_sm()
+ .gap_px()
+ .border_1()
+ .border_color(cx.theme().colors().border)
+ .when(self.flipped, |this| this.flex_row_reverse())
+ .child(
+ IconButton::new("sidebar-toggle", sidebar_icon)
+ .icon_size(IconSize::Small)
+ .toggle_state(self.sidebar_selected)
+ .when_some(self.sidebar_tooltip, |this, tooltip| this.tooltip(tooltip))
+ .when_some(self.on_sidebar_click, |this, handler| {
+ this.on_click(handler)
+ }),
+ )
+ .child(div().h_4().w_px().bg(cx.theme().colors().border))
+ .child(
+ IconButton::new("thread-toggle", IconName::Thread)
+ .icon_size(IconSize::Small)
+ .toggle_state(self.thread_selected)
+ .when_some(self.thread_tooltip, |this, tooltip| this.tooltip(tooltip))
+ .when_some(self.on_thread_click, |this, handler| this.on_click(handler)),
+ )
+ }
+}
+
+impl Component for ThreadSidebarToggle {
+ fn scope() -> ComponentScope {
+ ComponentScope::Agent
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
+ let container = || div().p_2().bg(cx.theme().colors().status_bar_background);
+
+ let examples = vec![
+ single_example(
+ "Both Unselected",
+ container()
+ .child(ThreadSidebarToggle::new())
+ .into_any_element(),
+ ),
+ single_example(
+ "Sidebar Selected",
+ container()
+ .child(ThreadSidebarToggle::new().sidebar_selected(true))
+ .into_any_element(),
+ ),
+ single_example(
+ "Thread Selected",
+ container()
+ .child(ThreadSidebarToggle::new().thread_selected(true))
+ .into_any_element(),
+ ),
+ single_example(
+ "Both Selected",
+ container()
+ .child(
+ ThreadSidebarToggle::new()
+ .sidebar_selected(true)
+ .thread_selected(true),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "Flipped",
+ container()
+ .child(
+ ThreadSidebarToggle::new()
+ .sidebar_selected(true)
+ .thread_selected(true)
+ .flipped(true),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "With Tooltips",
+ container()
+ .child(
+ ThreadSidebarToggle::new()
+ .sidebar_tooltip(Tooltip::text("Toggle Sidebar"))
+ .thread_tooltip(Tooltip::text("Toggle Thread")),
+ )
+ .into_any_element(),
+ ),
+ ];
+
+ Some(example_group(examples).into_any_element())
+ }
+}