1use std::sync::Arc;
2
3use assistant_tool::{ToolSource, ToolWorkingSet};
4use gpui::Entity;
5use scripting_tool::ScriptingTool;
6use ui::{prelude::*, ContextMenu, IconButtonShape, PopoverMenu, Tooltip};
7
8pub struct ToolSelector {
9 tools: Arc<ToolWorkingSet>,
10}
11
12impl ToolSelector {
13 pub fn new(tools: Arc<ToolWorkingSet>, _cx: &mut Context<Self>) -> Self {
14 Self { tools }
15 }
16
17 fn build_context_menu(
18 &self,
19 window: &mut Window,
20 cx: &mut Context<Self>,
21 ) -> Entity<ContextMenu> {
22 ContextMenu::build(window, cx, |mut menu, _window, cx| {
23 let tools_by_source = self.tools.tools_by_source(cx);
24
25 let all_tools_enabled = self.tools.are_all_tools_enabled();
26 menu = menu.header("Tools").toggleable_entry(
27 "All Tools",
28 all_tools_enabled,
29 IconPosition::End,
30 None,
31 {
32 let tools = self.tools.clone();
33 move |_window, cx| {
34 if all_tools_enabled {
35 tools.disable_all_tools(cx);
36 } else {
37 tools.enable_all_tools();
38 }
39 }
40 },
41 );
42
43 for (source, tools) in tools_by_source {
44 let mut tools = tools
45 .into_iter()
46 .map(|tool| {
47 let source = tool.source();
48 let name = tool.name().into();
49 let is_enabled = self.tools.is_enabled(&source, &name);
50
51 (source, name, is_enabled)
52 })
53 .collect::<Vec<_>>();
54
55 if ToolSource::Native == source {
56 tools.push((
57 ToolSource::Native,
58 ScriptingTool::NAME.into(),
59 self.tools.is_scripting_tool_enabled(),
60 ));
61 tools.sort_by(|(_, name_a, _), (_, name_b, _)| name_a.cmp(name_b));
62 }
63
64 menu = match source {
65 ToolSource::Native => menu.header("Zed"),
66 ToolSource::ContextServer { id } => menu.separator().header(id),
67 };
68
69 for (source, name, is_enabled) in tools {
70 menu =
71 menu.toggleable_entry(name.clone(), is_enabled, IconPosition::End, None, {
72 let tools = self.tools.clone();
73 move |_window, _cx| {
74 if name.as_ref() == ScriptingTool::NAME {
75 if is_enabled {
76 tools.disable_scripting_tool();
77 } else {
78 tools.enable_scripting_tool();
79 }
80 } else {
81 if is_enabled {
82 tools.disable(source.clone(), &[name.clone()]);
83 } else {
84 tools.enable(source.clone(), &[name.clone()]);
85 }
86 }
87 }
88 });
89 }
90 }
91
92 menu
93 })
94 }
95}
96
97impl Render for ToolSelector {
98 fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
99 let this = cx.entity().clone();
100 PopoverMenu::new("tool-selector")
101 .menu(move |window, cx| {
102 Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
103 })
104 .trigger_with_tooltip(
105 IconButton::new("tool-selector-button", IconName::SettingsAlt)
106 .shape(IconButtonShape::Square)
107 .icon_size(IconSize::Small)
108 .icon_color(Color::Muted),
109 Tooltip::text("Customize Tools"),
110 )
111 .anchor(gpui::Corner::BottomLeft)
112 }
113}