Skip to repository content278 lines · 8.7 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:06:40.985Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
sandbox_status_tooltip.rs
1use std::path::PathBuf;
2
3use file_icons::FileIcons;
4use gpui::{AnyElement, App};
5use ui::{Divider, prelude::*};
6
7#[derive(Clone)]
8pub enum SandboxRow {
9 Message(SharedString),
10 Path(PathBuf),
11 Domain(SharedString),
12}
13
14impl SandboxRow {
15 pub fn message(message: impl Into<SharedString>) -> Self {
16 Self::Message(message.into())
17 }
18
19 pub fn path(path: impl Into<PathBuf>) -> Self {
20 Self::Path(path.into())
21 }
22
23 pub fn domain(domain: impl Into<SharedString>) -> Self {
24 Self::Domain(domain.into())
25 }
26
27 fn render(self, cx: &App) -> AnyElement {
28 let icon_basic = |icon_name: IconName| {
29 Icon::new(icon_name)
30 .color(Color::Muted)
31 .size(IconSize::Small)
32 };
33
34 let (icon, label) = match self {
35 SandboxRow::Message(message) => {
36 return Label::new(message)
37 .size(LabelSize::XSmall)
38 .color(Color::Muted)
39 .into_any_element();
40 }
41 SandboxRow::Path(path) => {
42 let icon = FileIcons::get_icon(&path, cx)
43 .map(|icon| {
44 Icon::from_path(icon)
45 .color(Color::Muted)
46 .size(IconSize::Small)
47 })
48 .unwrap_or_else(|| icon_basic(IconName::Folder));
49 (icon, path.display().to_string())
50 }
51 SandboxRow::Domain(domain) => (icon_basic(IconName::Public), domain.to_string()),
52 };
53
54 h_flex()
55 .items_start()
56 .min_w_0()
57 .gap_1p5()
58 .child(icon)
59 .child(
60 div()
61 .flex_1()
62 .min_w_0()
63 .overflow_hidden()
64 .child(Label::new(label).size(LabelSize::XSmall).buffer_font(cx)),
65 )
66 .into_any_element()
67 }
68}
69
70#[derive(Clone)]
71pub struct SandboxGroup {
72 heading: SharedString,
73 rows: Vec<SandboxRow>,
74}
75
76impl SandboxGroup {
77 pub fn new(heading: impl Into<SharedString>) -> Self {
78 Self {
79 heading: heading.into(),
80 rows: Vec::new(),
81 }
82 }
83
84 pub fn row(mut self, row: SandboxRow) -> Self {
85 self.rows.push(row);
86 self
87 }
88
89 pub fn rows(mut self, rows: impl IntoIterator<Item = SandboxRow>) -> Self {
90 self.rows.extend(rows);
91 self
92 }
93
94 fn render(self, cx: &App) -> impl IntoElement {
95 v_flex()
96 .gap_1p5()
97 .child(
98 Label::new(self.heading)
99 .size(LabelSize::Small)
100 .color(Color::Muted),
101 )
102 .children(self.rows.into_iter().map(|row| row.render(cx)))
103 }
104}
105
106#[derive(Clone)]
107pub struct SandboxSection {
108 title: SharedString,
109 groups: Vec<SandboxGroup>,
110}
111
112impl SandboxSection {
113 pub fn new(title: impl Into<SharedString>) -> Self {
114 Self {
115 title: title.into(),
116 groups: Vec::new(),
117 }
118 }
119
120 pub fn group(mut self, group: SandboxGroup) -> Self {
121 self.groups.push(group);
122 self
123 }
124
125 fn render(self, cx: &App) -> AnyElement {
126 v_flex()
127 .gap_2()
128 .child(Label::new(self.title).size(LabelSize::Small))
129 .children(self.groups.into_iter().map(|group| {
130 v_flex()
131 .gap_2()
132 .child(Divider::horizontal())
133 .child(group.render(cx))
134 }))
135 .into_any_element()
136 }
137}
138
139#[derive(Clone, IntoElement, RegisterComponent)]
140pub enum SandboxStatusTooltip {
141 Enabled {
142 settings: SandboxSection,
143 thread: Option<SandboxSection>,
144 },
145 DisabledForThread {
146 settings: SandboxSection,
147 },
148 DisabledInSettings,
149}
150
151impl SandboxStatusTooltip {
152 pub fn enabled(settings: SandboxSection, thread: Option<SandboxSection>) -> Self {
153 Self::Enabled { settings, thread }
154 }
155
156 pub fn disabled_for_thread(settings: SandboxSection) -> Self {
157 Self::DisabledForThread { settings }
158 }
159
160 pub fn disabled_in_settings() -> Self {
161 Self::DisabledInSettings
162 }
163}
164
165impl RenderOnce for SandboxStatusTooltip {
166 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
167 let content = match self {
168 SandboxStatusTooltip::DisabledInSettings => v_flex()
169 .child(
170 Label::new("You have sandboxing disabled in settings.")
171 .size(LabelSize::Small)
172 .color(Color::Muted),
173 )
174 .into_any_element(),
175 SandboxStatusTooltip::DisabledForThread { settings } => v_flex()
176 .gap_1()
177 .child(div().opacity(0.5).child(settings.render(cx)))
178 .child(Divider::horizontal())
179 .child(Label::new("Sandboxing is disabled for this thread").size(LabelSize::Small))
180 .into_any_element(),
181 SandboxStatusTooltip::Enabled { settings, thread } => v_flex()
182 .gap_2()
183 .child(settings.render(cx))
184 .children(thread.map(|thread| {
185 v_flex()
186 .gap_2()
187 .child(Divider::horizontal())
188 .child(thread.render(cx))
189 }))
190 .into_any_element(),
191 };
192
193 v_flex()
194 .w(rems_from_px(280.))
195 .gap_1()
196 .child(Label::new("Sandboxing"))
197 .child(content)
198 }
199}
200
201impl Component for SandboxStatusTooltip {
202 fn scope() -> ComponentScope {
203 ComponentScope::Agent
204 }
205
206 fn name() -> &'static str {
207 "Sandbox Status Tooltip"
208 }
209
210 fn description() -> &'static str {
211 "The tooltip shown on the sandboxing lock icon in the agent panel, \
212 describing the filesystem, network, and Git access granted to the \
213 agent for each of the possible sandbox states."
214 }
215
216 fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
217 let settings_section = SandboxSection::new("Defined in your settings:")
218 .group(SandboxGroup::new("Write Access").rows([
219 SandboxRow::path("/Users/you/project"),
220 SandboxRow::path("/tmp (isolated)"),
221 ]))
222 .group(SandboxGroup::new("Network Access").rows([
223 SandboxRow::domain("github.com"),
224 SandboxRow::domain("*.npmjs.org"),
225 ]));
226
227 let thread_section = SandboxSection::new("Allowed for this thread:")
228 .group(
229 SandboxGroup::new("Write Access").row(SandboxRow::path("/Users/you/project/build")),
230 )
231 .group(SandboxGroup::new("Network Access").row(SandboxRow::message("None")));
232
233 let unrestricted_section = SandboxSection::new("Defined in your settings:")
234 .group(SandboxGroup::new("Write Access").row(SandboxRow::message(
235 "All paths except protected Git metadata",
236 )))
237 .group(
238 SandboxGroup::new("Network Access")
239 .row(SandboxRow::message("All domains (unrestricted)")),
240 );
241
242 let container = || div().p_2().elevation_2(cx).max_w_112();
243
244 v_flex()
245 .gap_4()
246 .child(example_group(vec![
247 single_example(
248 "Enabled",
249 container()
250 .child(SandboxStatusTooltip::enabled(
251 settings_section.clone(),
252 Some(thread_section),
253 ))
254 .into_any_element(),
255 ),
256 single_example(
257 "Enabled (unrestricted, no overrides)",
258 container()
259 .child(SandboxStatusTooltip::enabled(unrestricted_section, None))
260 .into_any_element(),
261 ),
262 single_example(
263 "Disabled for thread",
264 container()
265 .child(SandboxStatusTooltip::disabled_for_thread(settings_section))
266 .into_any_element(),
267 ),
268 single_example(
269 "Disabled in settings",
270 container()
271 .child(SandboxStatusTooltip::disabled_in_settings())
272 .into_any_element(),
273 ),
274 ]))
275 .into_any_element()
276 }
277}
278