1use assistant_tooling::ToolRegistry;
2use gpui::{percentage, prelude::*, Animation, AnimationExt, Model, Transformation};
3use semantic_index::{ProjectIndex, Status};
4use std::{sync::Arc, time::Duration};
5use ui::{prelude::*, ButtonLike, Color, Icon, IconName, Indicator, Tooltip};
6
7use crate::tools::ProjectIndexTool;
8
9pub struct ProjectIndexButton {
10 project_index: Model<ProjectIndex>,
11 tool_registry: Arc<ToolRegistry>,
12}
13
14impl ProjectIndexButton {
15 pub fn new(
16 project_index: Model<ProjectIndex>,
17 tool_registry: Arc<ToolRegistry>,
18 cx: &mut ViewContext<Self>,
19 ) -> Self {
20 cx.subscribe(&project_index, |_this, _, _status: &Status, cx| {
21 cx.notify();
22 })
23 .detach();
24 Self {
25 project_index,
26 tool_registry,
27 }
28 }
29
30 pub fn set_enabled(&mut self, enabled: bool) {
31 self.tool_registry
32 .set_tool_enabled::<ProjectIndexTool>(enabled);
33 }
34}
35
36impl Render for ProjectIndexButton {
37 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
38 let status = self.project_index.read(cx).status();
39 let is_enabled = self.tool_registry.is_tool_enabled::<ProjectIndexTool>();
40
41 let icon = if is_enabled {
42 match status {
43 Status::Idle => Icon::new(IconName::Code)
44 .size(IconSize::XSmall)
45 .color(Color::Default),
46 Status::Loading => Icon::new(IconName::Code)
47 .size(IconSize::XSmall)
48 .color(Color::Muted),
49 Status::Scanning { .. } => Icon::new(IconName::Code)
50 .size(IconSize::XSmall)
51 .color(Color::Muted),
52 }
53 } else {
54 Icon::new(IconName::Code)
55 .size(IconSize::XSmall)
56 .color(Color::Disabled)
57 };
58
59 let indicator = if is_enabled {
60 match status {
61 Status::Idle => Some(Indicator::dot().color(Color::Success)),
62 Status::Scanning { .. } => Some(Indicator::dot().color(Color::Warning)),
63 Status::Loading => Some(Indicator::icon(
64 Icon::new(IconName::Spinner)
65 .color(Color::Accent)
66 .with_animation(
67 "arrow-circle",
68 Animation::new(Duration::from_secs(2)).repeat(),
69 |icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
70 ),
71 )),
72 }
73 } else {
74 None
75 };
76
77 ButtonLike::new("project-index")
78 .child(
79 ui::IconWithIndicator::new(icon, indicator)
80 .indicator_border_color(Some(gpui::transparent_black())),
81 )
82 .tooltip({
83 move |cx| {
84 let (tooltip, meta) = match (is_enabled, status) {
85 (false, _) => (
86 "Project index disabled".to_string(),
87 Some("Click to enable".to_string()),
88 ),
89 (_, Status::Idle) => (
90 "Project index ready".to_string(),
91 Some("Click to disable".to_string()),
92 ),
93 (_, Status::Loading) => ("Project index loading...".to_string(), None),
94 (_, Status::Scanning { remaining_count }) => (
95 "Project index scanning...".to_string(),
96 Some(format!("{} remaining...", remaining_count)),
97 ),
98 };
99
100 if let Some(meta) = meta {
101 Tooltip::with_meta(tooltip, None, meta, cx)
102 } else {
103 Tooltip::text(tooltip, cx)
104 }
105 }
106 })
107 .on_click(cx.listener(move |this, _, cx| {
108 this.set_enabled(!is_enabled);
109 cx.notify();
110 }))
111 }
112}