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 for (source, tools) in tools_by_source {
26 let mut tools = tools
27 .into_iter()
28 .map(|tool| {
29 let source = tool.source();
30 let name = tool.name().into();
31 let is_enabled = self.tools.is_enabled(&source, &name);
32
33 (source, name, is_enabled)
34 })
35 .collect::<Vec<_>>();
36
37 if ToolSource::Native == source {
38 tools.push((
39 ToolSource::Native,
40 ScriptingTool::NAME.into(),
41 self.tools.is_scripting_tool_enabled(),
42 ));
43 tools.sort_by(|(_, name_a, _), (_, name_b, _)| name_a.cmp(name_b));
44 }
45
46 menu = match source {
47 ToolSource::Native => menu.header("Zed"),
48 ToolSource::ContextServer { id } => menu.separator().header(id),
49 };
50
51 for (source, name, is_enabled) in tools {
52 menu =
53 menu.toggleable_entry(name.clone(), is_enabled, IconPosition::End, None, {
54 let tools = self.tools.clone();
55 move |_window, _cx| {
56 if name.as_ref() == ScriptingTool::NAME {
57 if is_enabled {
58 tools.disable_scripting_tool();
59 } else {
60 tools.enable_scripting_tool();
61 }
62 } else {
63 if is_enabled {
64 tools.disable(source.clone(), &[name.clone()]);
65 } else {
66 tools.enable(source.clone(), &[name.clone()]);
67 }
68 }
69 }
70 });
71 }
72 }
73
74 menu
75 })
76 }
77}
78
79impl Render for ToolSelector {
80 fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
81 let this = cx.entity().clone();
82 PopoverMenu::new("tool-selector")
83 .menu(move |window, cx| {
84 Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
85 })
86 .trigger_with_tooltip(
87 IconButton::new("tool-selector-button", IconName::SettingsAlt)
88 .shape(IconButtonShape::Square)
89 .icon_size(IconSize::Small)
90 .icon_color(Color::Muted),
91 Tooltip::text("Customize Tools"),
92 )
93 .anchor(gpui::Corner::BottomLeft)
94 }
95}