1use crate::attachments::ActiveEditorAttachmentTool;
2use assistant_tooling::AttachmentRegistry;
3use editor::Editor;
4use gpui::{prelude::*, Subscription, View};
5use std::sync::Arc;
6use ui::{prelude::*, ButtonLike, Color, Icon, IconName, Tooltip};
7use workspace::Workspace;
8
9#[derive(Clone)]
10enum Status {
11 ActiveFile(String),
12 #[allow(dead_code)]
13 NoFile,
14}
15
16pub struct ActiveFileButton {
17 attachment_registry: Arc<AttachmentRegistry>,
18 status: Status,
19 #[allow(dead_code)]
20 workspace_subscription: Subscription,
21}
22
23impl ActiveFileButton {
24 pub fn new(
25 attachment_registry: Arc<AttachmentRegistry>,
26 workspace: View<Workspace>,
27 cx: &mut ViewContext<Self>,
28 ) -> Self {
29 let workspace_subscription = cx.subscribe(&workspace, Self::handle_workspace_event);
30
31 cx.defer(move |this, cx| this.update_active_buffer(workspace.clone(), cx));
32
33 Self {
34 attachment_registry,
35 status: Status::NoFile,
36 workspace_subscription,
37 }
38 }
39
40 pub fn set_enabled(&mut self, enabled: bool) {
41 self.attachment_registry
42 .set_attachment_tool_enabled::<ActiveEditorAttachmentTool>(enabled);
43 }
44
45 pub fn update_active_buffer(&mut self, workspace: View<Workspace>, cx: &mut ViewContext<Self>) {
46 let active_buffer = workspace
47 .read(cx)
48 .active_item(cx)
49 .and_then(|item| Some(item.act_as::<Editor>(cx)?.read(cx).buffer().clone()));
50
51 if let Some(buffer) = active_buffer {
52 let buffer = buffer.read(cx);
53
54 if let Some(singleton) = buffer.as_singleton() {
55 let singleton = singleton.read(cx);
56
57 let filename: String = singleton
58 .file()
59 .map(|file| file.path().to_string_lossy())
60 .unwrap_or("Untitled".into())
61 .into();
62
63 self.status = Status::ActiveFile(filename);
64 }
65 }
66 }
67
68 fn handle_workspace_event(
69 &mut self,
70 workspace: View<Workspace>,
71 event: &workspace::Event,
72 cx: &mut ViewContext<Self>,
73 ) {
74 if let workspace::Event::ActiveItemChanged = event {
75 self.update_active_buffer(workspace, cx);
76 }
77 }
78}
79
80impl Render for ActiveFileButton {
81 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
82 let is_enabled = self
83 .attachment_registry
84 .is_attachment_tool_enabled::<ActiveEditorAttachmentTool>();
85
86 let icon = if is_enabled {
87 Icon::new(IconName::File)
88 .size(IconSize::XSmall)
89 .color(Color::Default)
90 } else {
91 Icon::new(IconName::File)
92 .size(IconSize::XSmall)
93 .color(Color::Disabled)
94 };
95
96 let indicator = None;
97
98 let status = self.status.clone();
99
100 ButtonLike::new("active-file-button")
101 .child(
102 ui::IconWithIndicator::new(icon, indicator)
103 .indicator_border_color(Some(gpui::transparent_black())),
104 )
105 .tooltip({
106 move |cx| {
107 let status = status.clone();
108 let (tooltip, meta) = match (is_enabled, status) {
109 (false, _) => (
110 "Active file disabled".to_string(),
111 Some("Click to enable".to_string()),
112 ),
113 (true, Status::ActiveFile(filename)) => (
114 format!("Active file {filename} enabled"),
115 Some("Click to disable".to_string()),
116 ),
117 (true, Status::NoFile) => {
118 ("No file active for conversation".to_string(), None)
119 }
120 };
121
122 if let Some(meta) = meta {
123 Tooltip::with_meta(tooltip, None, meta, cx)
124 } else {
125 Tooltip::text(tooltip, cx)
126 }
127 }
128 })
129 .on_click(cx.listener(move |this, _, cx| {
130 this.set_enabled(!is_enabled);
131 cx.notify();
132 }))
133 }
134}