diff --git a/crates/agent_ui/src/conversation_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs index ed4bbeda819..f43dfdaa347 100644 --- a/crates/agent_ui/src/conversation_view/thread_view.rs +++ b/crates/agent_ui/src/conversation_view/thread_view.rs @@ -4646,7 +4646,11 @@ impl ThreadView { // settings scope (greyed) for context above the disabled status. (ThreadSandbox::Sandboxed(settings_policy), ThreadSandbox::Unsandboxed) => { let settings = augment_settings_sandbox_policy(settings_policy, baseline); - SandboxStatusTooltip::disabled_for_thread(sandbox_section(&settings, true)) + SandboxStatusTooltip::disabled_for_thread(sandbox_section( + "Defined in your settings:", + &settings, + true, + )) } ( ThreadSandbox::Sandboxed(settings_policy), @@ -4655,8 +4659,11 @@ impl ThreadView { let settings = augment_settings_sandbox_policy(settings_policy, baseline); // Omit the per-thread section when it grants nothing extra. let thread = (!sandbox_policy_grants_nothing(&thread_policy)) - .then(|| sandbox_section(&thread_policy, false)); - SandboxStatusTooltip::enabled(sandbox_section(&settings, true), thread) + .then(|| sandbox_section("Allowed for this thread:", &thread_policy, false)); + SandboxStatusTooltip::enabled( + sandbox_section("Defined in your settings:", &settings, true), + thread, + ) } }; @@ -5625,25 +5632,28 @@ fn augment_settings_sandbox_policy( policy } -fn sandbox_section(policy: &SandboxPolicy, show_empty: bool) -> SandboxSection { +fn sandbox_section(title: &str, policy: &SandboxPolicy, show_empty: bool) -> SandboxSection { let write_empty = fs_grants_nothing(&policy.fs); let network_empty = network_grants_nothing(&policy.network); - // Git access is only surfaced when granted, so it never shows a "None" row. let git_empty = git_grants_nothing(&policy.git); - let mut section = SandboxSection::new(); + let mut section = SandboxSection::new(title.to_string()); + if show_empty || !write_empty { section = section.group(SandboxGroup::new("Write Access").rows(sandbox_fs_rows(&policy.fs))); } + if show_empty || !network_empty { section = section .group(SandboxGroup::new("Network Access").rows(sandbox_network_rows(&policy.network))); } + if !git_empty { section = section .group(SandboxGroup::new("Git Metadata Access").rows(sandbox_git_rows(&policy.git))); } + section } diff --git a/crates/agent_ui/src/ui/sandbox_status_tooltip.rs b/crates/agent_ui/src/ui/sandbox_status_tooltip.rs index 506d437b535..089492bca95 100644 --- a/crates/agent_ui/src/ui/sandbox_status_tooltip.rs +++ b/crates/agent_ui/src/ui/sandbox_status_tooltip.rs @@ -58,21 +58,21 @@ impl SandboxRow { }; h_flex() + .items_start() .min_w_0() - .w_full() - .gap_1() + .gap_1p5() .child(icon) .child( - Label::new(label) - .size(LabelSize::XSmall) - .buffer_font(cx) - .truncate_start(), + div() + .flex_1() + .min_w_0() + .overflow_hidden() + .child(Label::new(label).size(LabelSize::XSmall).buffer_font(cx)), ) .into_any_element() } } -/// A labelled group of rows (e.g. "Write Access", "Network Access"). #[derive(Clone)] pub struct SandboxGroup { heading: SharedString, @@ -99,7 +99,7 @@ impl SandboxGroup { fn render(self, cx: &App) -> impl IntoElement { v_flex() - .gap_1() + .gap_1p5() .child( Label::new(self.heading) .size(LabelSize::Small) @@ -109,14 +109,18 @@ impl SandboxGroup { } } -#[derive(Clone, Default)] +#[derive(Clone)] pub struct SandboxSection { + title: SharedString, groups: Vec, } impl SandboxSection { - pub fn new() -> Self { - Self::default() + pub fn new(title: impl Into) -> Self { + Self { + title: title.into(), + groups: Vec::new(), + } } pub fn group(mut self, group: SandboxGroup) -> Self { @@ -125,14 +129,16 @@ impl SandboxSection { } fn render(self, cx: &App) -> AnyElement { - let mut section = v_flex().gap_1(); - for (index, group) in self.groups.into_iter().enumerate() { - if index > 0 { - section = section.child(Divider::horizontal()); - } - section = section.child(group.render(cx)); - } - section.into_any_element() + v_flex() + .gap_2() + .child(Label::new(self.title).size(LabelSize::Small)) + .children(self.groups.into_iter().map(|group| { + v_flex() + .gap_2() + .child(Divider::horizontal()) + .child(group.render(cx)) + })) + .into_any_element() } } @@ -179,9 +185,11 @@ impl RenderOnce for SandboxStatusTooltip { .child(Label::new("Sandboxing is disabled for this thread").size(LabelSize::Small)) .into_any_element(), SandboxStatusTooltip::Enabled { settings, thread } => v_flex() + .gap_2() .child(settings.render(cx)) .children(thread.map(|thread| { v_flex() + .gap_2() .child(Divider::horizontal()) .child(thread.render(cx)) })) @@ -189,7 +197,7 @@ impl RenderOnce for SandboxStatusTooltip { }; v_flex() - .min_w(rems(15.)) + .w(rems_from_px(280.)) .gap_1() .child(Label::new("Sandboxing")) .child(content) @@ -212,7 +220,7 @@ impl Component for SandboxStatusTooltip { } fn preview(_window: &mut Window, cx: &mut App) -> AnyElement { - let settings_section = SandboxSection::new() + let settings_section = SandboxSection::new("Defined in your settings:") .group(SandboxGroup::new("Write Access").rows([ SandboxRow::path("/Users/you/project"), SandboxRow::path("/tmp (isolated)"), @@ -222,7 +230,7 @@ impl Component for SandboxStatusTooltip { SandboxRow::domain("*.npmjs.org"), ])); - let thread_section = SandboxSection::new() + let thread_section = SandboxSection::new("Allowed for this thread:") .group( SandboxGroup::new("Write Access").row(SandboxRow::path("/Users/you/project/build")), ) @@ -232,7 +240,7 @@ impl Component for SandboxStatusTooltip { .row(SandboxRow::git("/Users/you/project/.git")), ); - let unrestricted_section = SandboxSection::new() + let unrestricted_section = SandboxSection::new("Defined in your settings:") .group( SandboxGroup::new("Write Access") .row(SandboxRow::message("All paths (unrestricted)")),