Gate plugins in private profiles

This commit is contained in:
2026-05-09 00:44:15 -04:00
parent 7797203bbf
commit 96732c8932
8 changed files with 236 additions and 31 deletions
@@ -166,25 +166,24 @@ fn render_plugin_catalog_list(
.overflow_y_scrollbar()
.border_t_1()
.border_color(rgb(colors::HAIRLINE))
.children(
snapshot
.installed_plugins
.iter()
.enumerate()
.map(|(index, plugin)| render_plugin_catalog_row(index, plugin, cx)),
)
.children(snapshot.installed_plugins.iter().enumerate().map(|(index, plugin)| {
render_plugin_catalog_row(index, plugin, &snapshot.active_profile_kind, cx)
}))
.into_any_element()
}
fn render_plugin_catalog_row(
index: usize,
plugin: &InstalledPlugin,
profile_kind: &ely_domain::ProfileKind,
cx: &mut Context<ElyShell>,
) -> AnyElement {
let detail_route = format!("ely://plugin/{}", plugin.id().as_str());
let high_risk_count = plugin.manifest().high_risk_permissions().count();
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
let status_label = if plugin.enabled() { "Enabled" } else { "Disabled" };
let status_color =
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
let status_label =
if plugin.enabled_for_profile(profile_kind) { "Enabled" } else { "Disabled" };
div()
.py_3()
@@ -251,7 +250,11 @@ fn render_plugin_catalog_row(
}
fn enabled_plugin_count(snapshot: &BrowserSnapshot) -> usize {
snapshot.installed_plugins.iter().filter(|plugin| plugin.enabled()).count()
snapshot
.installed_plugins
.iter()
.filter(|plugin| plugin.enabled_for_profile(&snapshot.active_profile_kind))
.count()
}
fn high_risk_plugin_count(snapshot: &BrowserSnapshot) -> usize {
@@ -1,6 +1,6 @@
use ely_browser_core::{BrowserSnapshot, InstalledPlugin};
use ely_design_system::colors;
use ely_domain::{PluginId, PluginManifest, PluginPermission, PluginPermissionRisk};
use ely_domain::{PluginId, PluginManifest, PluginPermission, PluginPermissionRisk, ProfileKind};
use gpui::prelude::FluentBuilder;
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
use gpui_component::{
@@ -30,10 +30,14 @@ impl ElyShell {
.flex_col()
.gap_5()
.when_some(plugin, |this, plugin| {
this.child(self.render_plugin_detail_header(plugin, cx))
.child(render_plugin_security_summary(plugin))
.child(render_plugin_manifest_rows(plugin.manifest()))
.child(render_plugin_permission_list(plugin.manifest()))
this.child(self.render_plugin_detail_header(
plugin,
&snapshot.active_profile_kind,
cx,
))
.child(render_plugin_security_summary(plugin))
.child(render_plugin_manifest_rows(plugin.manifest()))
.child(render_plugin_permission_list(plugin.manifest()))
})
.when(plugin.is_none(), |this| {
this.child(render_missing_plugin_detail(plugin_id.as_ref(), cx))
@@ -44,14 +48,19 @@ impl ElyShell {
fn render_plugin_detail_header(
&mut self,
plugin: &InstalledPlugin,
profile_kind: &ProfileKind,
cx: &mut Context<Self>,
) -> AnyElement {
let plugin_id = plugin.id().clone();
let target_enabled = !plugin.enabled();
let status_label = if plugin.enabled() { "Enabled" } else { "Disabled" };
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
let private_allowed = plugin.private_window_allowed();
let status_label = plugin_status_label(plugin, profile_kind);
let status_color =
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
let action_label = if plugin.enabled() { "Disable" } else { "Enable" };
let action_icon = if plugin.enabled() { IconName::CircleX } else { IconName::Check };
let private_action_label = if private_allowed { "Block Private" } else { "Allow Private" };
let private_action_icon = if private_allowed { IconName::CircleX } else { IconName::Check };
div()
.flex()
@@ -82,6 +91,24 @@ impl ElyShell {
.gap_3()
.text_xs()
.child(div().font_semibold().text_color(rgb(status_color)).child(status_label))
.child(
Button::new("toggle-plugin-detail-private")
.ghost()
.xsmall()
.icon(private_action_icon)
.label(private_action_label)
.tooltip("Set Private Window Permission")
.on_click(cx.listener({
let plugin_id = plugin_id.clone();
move |shell, _, _, cx| {
shell.set_plugin_private_window_allowed(
plugin_id.clone(),
!private_allowed,
cx,
);
}
})),
)
.child(
Button::new("toggle-plugin-detail-enabled")
.xsmall()
@@ -108,6 +135,17 @@ impl ElyShell {
}
}
fn plugin_status_label(plugin: &InstalledPlugin, profile_kind: &ProfileKind) -> &'static str {
if !plugin.enabled() {
return "Disabled";
}
match profile_kind {
ProfileKind::Standard => "Enabled",
ProfileKind::Private if plugin.private_window_allowed() => "Private Enabled",
ProfileKind::Private => "Private Off",
}
}
fn render_plugin_security_summary(plugin: &InstalledPlugin) -> AnyElement {
let manifest = plugin.manifest();
div()
@@ -1,6 +1,6 @@
use ely_browser_core::{BrowserSnapshot, InstalledPlugin, PluginAuditAction, PluginAuditEvent};
use ely_design_system::colors;
use ely_domain::PluginId;
use ely_domain::{PluginId, ProfileKind};
use gpui::prelude::FluentBuilder;
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
use gpui_component::{
@@ -252,7 +252,7 @@ fn render_plugin_list(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) ->
.installed_plugins
.iter()
.enumerate()
.map(|(index, plugin)| render_plugin_row(index, plugin, cx))
.map(|(index, plugin)| render_plugin_row(index, plugin, &snapshot.active_profile_kind, cx))
.collect::<Vec<_>>();
div()
@@ -267,11 +267,13 @@ fn render_plugin_list(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) ->
fn render_plugin_row(
index: usize,
plugin: &InstalledPlugin,
profile_kind: &ProfileKind,
cx: &mut Context<ElyShell>,
) -> AnyElement {
let high_risk_count = plugin.manifest().high_risk_permissions().count();
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
let status_label = if plugin.enabled() { "Enabled" } else { "Disabled" };
let status_color =
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
let status_label = plugin_status_label(plugin, profile_kind);
let plugin_id = plugin.id().clone();
let plugin_name = plugin.manifest().name().to_string();
let target_enabled = !plugin.enabled();
@@ -323,11 +325,23 @@ fn render_plugin_row(
target_enabled,
cx,
))
.child(render_plugin_private_button(index, plugin, plugin_id.clone(), cx))
.child(render_plugin_uninstall_button(index, plugin_id, plugin_name, cx)),
)
.into_any_element()
}
fn plugin_status_label(plugin: &InstalledPlugin, profile_kind: &ProfileKind) -> &'static str {
if !plugin.enabled() {
return "Disabled";
}
match profile_kind {
ProfileKind::Standard => "Enabled",
ProfileKind::Private if plugin.private_window_allowed() => "Private Enabled",
ProfileKind::Private => "Private Off",
}
}
fn render_plugin_state_button(
index: usize,
plugin: &InstalledPlugin,
@@ -354,6 +368,28 @@ fn render_plugin_state_button(
}
}
fn render_plugin_private_button(
index: usize,
plugin: &InstalledPlugin,
plugin_id: PluginId,
cx: &mut Context<ElyShell>,
) -> AnyElement {
let allowed = plugin.private_window_allowed();
let label = if allowed { "Block Private" } else { "Allow Private" };
let icon = if allowed { IconName::CircleX } else { IconName::Check };
Button::new(("set-plugin-private", index))
.ghost()
.xsmall()
.icon(icon)
.label(label)
.tooltip("Set Private Window Permission")
.on_click(cx.listener(move |shell, _, _, cx| {
shell.set_plugin_private_window_allowed(plugin_id.clone(), !allowed, cx);
}))
.into_any_element()
}
fn render_plugin_uninstall_button(
index: usize,
plugin_id: PluginId,
@@ -423,6 +459,8 @@ fn plugin_audit_action_label(action: &PluginAuditAction) -> &'static str {
PluginAuditAction::Installed => "Installed",
PluginAuditAction::Enabled => "Enabled",
PluginAuditAction::Disabled => "Disabled",
PluginAuditAction::PrivateWindowAllowed => "Private Allowed",
PluginAuditAction::PrivateWindowBlocked => "Private Blocked",
PluginAuditAction::Uninstalled => "Uninstalled",
}
}
@@ -119,11 +119,9 @@ fn render_task_table(snapshot: &BrowserSnapshot) -> AnyElement {
})
.when(!snapshot.installed_plugins.is_empty(), |this| {
this.child(render_section_header("Plugins")).children(
snapshot
.installed_plugins
.iter()
.enumerate()
.map(|(index, plugin)| render_plugin_task_row(index, plugin)),
snapshot.installed_plugins.iter().enumerate().map(|(index, plugin)| {
render_plugin_task_row(index, plugin, &snapshot.active_profile_kind)
}),
)
})
.into_any_element()
@@ -169,9 +167,14 @@ fn render_download_task_row(index: usize, entry: &DownloadEntry) -> AnyElement {
)
}
fn render_plugin_task_row(index: usize, plugin: &InstalledPlugin) -> AnyElement {
let status = if plugin.enabled() { "Enabled" } else { "Disabled" };
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
fn render_plugin_task_row(
index: usize,
plugin: &InstalledPlugin,
profile_kind: &ely_domain::ProfileKind,
) -> AnyElement {
let status = if plugin.enabled_for_profile(profile_kind) { "Enabled" } else { "Disabled" };
let status_color =
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
let detail = format!(
"{} permissions - {} contributions",
plugin.manifest().permissions().len(),
+17
View File
@@ -170,6 +170,23 @@ impl ElyShell {
cx.notify();
}
pub(super) fn set_plugin_private_window_allowed(
&mut self,
plugin_id: PluginId,
allowed: bool,
cx: &mut Context<Self>,
) {
let result = match &mut self.state {
ShellState::Ready(core) => core
.set_plugin_private_window_allowed(&plugin_id, allowed)
.map_err(|error| error.to_string()),
ShellState::StartupError(message) => Err(message.clone()),
};
self.plugin_install_error = result.err();
cx.notify();
}
fn uninstall_plugin(&mut self, plugin_id: PluginId, cx: &mut Context<Self>) {
let result = match &mut self.state {
ShellState::Ready(core) => PluginPackageStore::application()