From e604ef3af914b249867058e4f11e045e38dfee88 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 28 Oct 2025 14:48:07 -0400 Subject: [PATCH] Add a setting to prevent sharing projects in public channels (#41395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds a setting to prevent projects from being shared in public channels. This can be enabled by adding the following to the project settings (`.zed/settings.json`): ```json { "prevent_sharing_in_public_channels": true } ``` This will then disable the "Share" button when not in a private channel: Screenshot 2025-10-28 at 2 28 10 PM Release Notes: - collaboration: Added a `prevent_sharing_in_public_channels` project setting for preventing projects from being shared in public channels. --- Cargo.lock | 1 + .../settings/src/settings_content/project.rs | 6 ++++ crates/settings/src/vscode_import.rs | 1 + crates/title_bar/Cargo.toml | 1 + crates/title_bar/src/collab.rs | 28 +++++++++++++++++++ crates/worktree/src/worktree_settings.rs | 3 ++ 6 files changed, 40 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 0ac3d661cffbf09675bd257e308b6bc95c16ae61..53ef3873b3d9704555bcf9035dcae7136618dbf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17439,6 +17439,7 @@ dependencies = [ "anyhow", "auto_update", "call", + "channel", "chrono", "client", "cloud_llm_client", diff --git a/crates/settings/src/settings_content/project.rs b/crates/settings/src/settings_content/project.rs index 6a77b815fa547d41e6f38541fe1d681c82b3347b..a6af4c617ae7e744926e0a7608ff3fddeb2d0cd2 100644 --- a/crates/settings/src/settings_content/project.rs +++ b/crates/settings/src/settings_content/project.rs @@ -64,6 +64,12 @@ pub struct WorktreeSettingsContent { #[serde(skip_serializing_if = "Maybe::is_unset")] pub project_name: Maybe, + /// Whether to prevent this project from being shared in public channels. + /// + /// Default: false + #[serde(default)] + pub prevent_sharing_in_public_channels: bool, + /// Completely ignore files matching globs from `file_scan_exclusions`. Overrides /// `file_scan_inclusions`. /// diff --git a/crates/settings/src/vscode_import.rs b/crates/settings/src/vscode_import.rs index f038f8fbbf2c1053868c9af61beae5ccdfe9bb02..71198883baf8051b0d6042746824a0d207c4fb76 100644 --- a/crates/settings/src/vscode_import.rs +++ b/crates/settings/src/vscode_import.rs @@ -855,6 +855,7 @@ impl VsCodeSettings { fn worktree_settings_content(&self) -> WorktreeSettingsContent { WorktreeSettingsContent { project_name: crate::Maybe::Unset, + prevent_sharing_in_public_channels: false, file_scan_exclusions: self .read_value("files.watcherExclude") .and_then(|v| v.as_array()) diff --git a/crates/title_bar/Cargo.toml b/crates/title_bar/Cargo.toml index 829dea3a55ba9fee7f2ede503139e1348dabc57f..6d5d0ce170e261deefca679953199597b2753981 100644 --- a/crates/title_bar/Cargo.toml +++ b/crates/title_bar/Cargo.toml @@ -30,6 +30,7 @@ test-support = [ anyhow.workspace = true auto_update.workspace = true call.workspace = true +channel.workspace = true chrono.workspace = true client.workspace = true cloud_llm_client.workspace = true diff --git a/crates/title_bar/src/collab.rs b/crates/title_bar/src/collab.rs index 5dd08ee3f9e132666520433db92279df559abdb0..070952d1cec346e4ec41e26f69895b65cd74f082 100644 --- a/crates/title_bar/src/collab.rs +++ b/crates/title_bar/src/collab.rs @@ -2,18 +2,22 @@ use std::rc::Rc; use std::sync::Arc; use call::{ActiveCall, ParticipantLocation, Room}; +use channel::ChannelStore; use client::{User, proto::PeerId}; use gpui::{ AnyElement, Hsla, IntoElement, MouseButton, Path, ScreenCaptureSource, Styled, WeakEntity, canvas, point, }; use gpui::{App, Task, Window, actions}; +use project::WorktreeSettings; use rpc::proto::{self}; +use settings::{Settings as _, SettingsLocation}; use theme::ActiveTheme; use ui::{ Avatar, AvatarAudioStatusIndicator, ContextMenu, ContextMenuItem, Divider, DividerColor, Facepile, PopoverMenu, SplitButton, SplitButtonStyle, TintColor, Tooltip, prelude::*, }; +use util::rel_path::RelPath; use workspace::notifications::DetachAndPromptErr; use crate::TitleBar; @@ -347,6 +351,11 @@ impl TitleBar { let can_share_projects = room.can_share_projects(); let screen_sharing_supported = cx.is_screen_capture_supported(); + let channel_store = ChannelStore::global(cx); + let channel = room + .channel_id() + .and_then(|channel_id| channel_store.read(cx).channel_for_id(channel_id).cloned()); + let mut children = Vec::new(); children.push( @@ -368,6 +377,20 @@ impl TitleBar { ); if is_local && can_share_projects && !is_connecting_to_project { + let is_sharing_disabled = channel.is_some_and(|channel| match channel.visibility { + proto::ChannelVisibility::Public => project.visible_worktrees(cx).any(|worktree| { + let worktree_id = worktree.read(cx).id(); + + let settings_location = Some(SettingsLocation { + worktree_id, + path: RelPath::empty(), + }); + + WorktreeSettings::get(settings_location, cx).prevent_sharing_in_public_channels + }), + proto::ChannelVisibility::Members => false, + }); + children.push( Button::new( "toggle_sharing", @@ -382,6 +405,11 @@ impl TitleBar { .selected_style(ButtonStyle::Tinted(TintColor::Accent)) .toggle_state(is_shared) .label_size(LabelSize::Small) + .when(is_sharing_disabled, |parent| { + parent.disabled(true).tooltip(Tooltip::text( + "This project may not be shared in a public channel.", + )) + }) .on_click(cx.listener(move |this, _, window, cx| { if is_shared { this.unshare_project(window, cx); diff --git a/crates/worktree/src/worktree_settings.rs b/crates/worktree/src/worktree_settings.rs index 9eddef8eaf43cecca949ea6f595c75795698ab38..e536256e51401e5cb4d9cbece0e5a52b3ff22b3c 100644 --- a/crates/worktree/src/worktree_settings.rs +++ b/crates/worktree/src/worktree_settings.rs @@ -11,6 +11,8 @@ use util::{ #[derive(Clone, PartialEq, Eq)] pub struct WorktreeSettings { pub project_name: Option, + /// Whether to prevent this project from being shared in public channels. + pub prevent_sharing_in_public_channels: bool, pub file_scan_inclusions: PathMatcher, pub file_scan_exclusions: PathMatcher, pub private_files: PathMatcher, @@ -51,6 +53,7 @@ impl Settings for WorktreeSettings { Self { project_name: worktree.project_name.into_inner(), + prevent_sharing_in_public_channels: worktree.prevent_sharing_in_public_channels, file_scan_exclusions: path_matchers(file_scan_exclusions, "file_scan_exclusions") .log_err() .unwrap_or_default(),