Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:06:40.985Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

sandbox_status_tooltip.rs

278 lines · 8.7 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.