diff --git a/crates/ely_app/src/shell/internal_pages.rs b/crates/ely_app/src/shell/internal_pages.rs index f74494b..ae636e2 100644 --- a/crates/ely_app/src/shell/internal_pages.rs +++ b/crates/ely_app/src/shell/internal_pages.rs @@ -15,6 +15,7 @@ mod notes; mod notes_markdown; mod plugin_catalog; mod plugin_details; +mod plugin_editors_pick; mod plugins; mod privacy_data_inventory; mod privacy_security; diff --git a/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs b/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs index 91f21ca..f168b9a 100644 --- a/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs +++ b/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs @@ -8,6 +8,7 @@ use gpui::{ }; use gpui_component::{IconName, scroll::ScrollableElement}; +use super::plugin_editors_pick::{action_button, render_editors_pick}; use super::{ElyShell, render_canvas_surface}; impl ElyShell { @@ -107,161 +108,6 @@ fn render_install_button(total: usize, cx: &mut Context) -> AnyElement .into_any_element() } -fn render_editors_pick( - snapshot: &BrowserSnapshot, - cx: &mut Context, -) -> AnyElement { - let featured = featured_plugin(snapshot); - - div() - .p(px(20.0)) - .rounded(px(16.0)) - .bg(linear_gradient( - 135.0, - linear_color_stop(hsla(0.0, 0.0, 1.0, 0.94), 0.0), - linear_color_stop(hsla(263.0 / 360.0, 0.6, 0.86, 0.65), 1.0), - )) - .flex() - .flex_col() - .gap(px(14.0)) - .child( - div() - .text_size(px(11.0)) - .text_color(rgb(colors::INK_3)) - .child("EDITOR'S PICK"), - ) - .child(render_featured_body(featured)) - .child(render_featured_actions(featured, cx)) - .into_any_element() -} - -fn render_featured_body(featured: Option<&InstalledPlugin>) -> AnyElement { - let (name, desc) = match featured { - Some(plugin) => ( - plugin.manifest().name().to_string(), - plugin.manifest().description().to_string(), - ), - None => ( - "Install your first plugin".to_string(), - "Drop a signed .rplug package onto ELY to extend the browser with sandboxed tools." - .to_string(), - ), - }; - - div() - .flex() - .gap(px(14.0)) - .child( - div() - .size(px(56.0)) - .rounded(px(14.0)) - .bg(linear_gradient( - 135.0, - linear_color_stop(hsla(341.0 / 360.0, 0.78, 0.85, 1.0), 0.0), - linear_color_stop(hsla(228.0 / 360.0, 1.0, 0.86, 1.0), 1.0), - )) - .flex() - .items_center() - .justify_center() - .text_size(px(28.0)) - .text_color(rgb(0xffffff)) - .child(name.chars().next().unwrap_or('✦').to_uppercase().to_string()), - ) - .child( - div() - .flex_1() - .min_w_0() - .flex() - .flex_col() - .gap_1() - .child( - div() - .text_size(px(15.0)) - .font_weight(FontWeight(600.0)) - .text_color(rgb(colors::INK)) - .child(name), - ) - .child( - div() - .text_size(px(12.5)) - .text_color(rgb(colors::INK_3)) - .child(desc), - ), - ) - .into_any_element() -} - -fn render_featured_actions( - featured: Option<&InstalledPlugin>, - cx: &mut Context, -) -> AnyElement { - if let Some(plugin) = featured { - let detail_route = format!("ely://plugin/{}", plugin.id().as_str()); - - div() - .flex() - .items_center() - .gap(px(8.0)) - .child(action_button( - "featured-open", - "Open detail", - cx, - move |shell, window, cx| { - shell.open_internal_tab(&detail_route, window, cx); - }, - )) - .child( - div() - .ml_auto() - .text_size(px(11.5)) - .text_color(rgb(colors::INK_3)) - .child(format!( - "{} permissions", - plugin.manifest().permissions().len() - )), - ) - .into_any_element() - } else { - div() - .flex() - .items_center() - .gap(px(8.0)) - .child(action_button( - "featured-install", - "Install", - cx, - |shell, window, cx| shell.choose_plugin_package(window, cx), - )) - .into_any_element() - } -} - -fn action_button( - id: &'static str, - label: &'static str, - cx: &mut Context, - handler: F, -) -> AnyElement -where - F: Fn(&mut ElyShell, &mut gpui::Window, &mut Context) + 'static, -{ - div() - .id(SharedString::from(id)) - .px(px(14.0)) - .py(px(7.0)) - .rounded(px(8.0)) - .bg(rgb(colors::INK)) - .text_size(px(12.0)) - .font_weight(FontWeight(500.0)) - .text_color(rgb(0xffffff)) - .cursor_pointer() - .hover(|style| style.opacity(0.92)) - .active(|style| style.opacity(0.78)) - .on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx))) - .child(label) - .into_any_element() -} - fn render_summary(snapshot: &BrowserSnapshot) -> AnyElement { div() .flex() @@ -481,14 +327,6 @@ fn render_plugin_card( .into_any_element() } -fn featured_plugin(snapshot: &BrowserSnapshot) -> Option<&InstalledPlugin> { - snapshot - .installed_plugins - .iter() - .find(|plugin| plugin.enabled_for_profile(&snapshot.active_profile_kind)) - .or_else(|| snapshot.installed_plugins.first()) -} - fn high_risk_plugin_count(snapshot: &BrowserSnapshot) -> usize { snapshot .installed_plugins diff --git a/crates/ely_app/src/shell/internal_pages/plugin_editors_pick.rs b/crates/ely_app/src/shell/internal_pages/plugin_editors_pick.rs new file mode 100644 index 0000000..ca044f8 --- /dev/null +++ b/crates/ely_app/src/shell/internal_pages/plugin_editors_pick.rs @@ -0,0 +1,171 @@ +use ely_browser_core::{BrowserSnapshot, InstalledPlugin}; +use ely_design_system::colors; +use gpui::{ + AnyElement, Context, FontWeight, InteractiveElement, IntoElement, ParentElement, SharedString, + StatefulInteractiveElement, Styled, div, hsla, linear_color_stop, linear_gradient, px, rgb, +}; + +use super::ElyShell; + +pub(super) fn render_editors_pick( + snapshot: &BrowserSnapshot, + cx: &mut Context, +) -> AnyElement { + let featured = featured_plugin(snapshot); + + div() + .p(px(20.0)) + .rounded(px(16.0)) + .bg(linear_gradient( + 135.0, + linear_color_stop(hsla(0.0, 0.0, 1.0, 0.94), 0.0), + linear_color_stop(hsla(263.0 / 360.0, 0.6, 0.86, 0.65), 1.0), + )) + .flex() + .flex_col() + .gap(px(14.0)) + .child( + div() + .text_size(px(11.0)) + .text_color(rgb(colors::INK_3)) + .child("EDITOR'S PICK"), + ) + .child(render_featured_body(featured)) + .child(render_featured_actions(featured, cx)) + .into_any_element() +} + +fn render_featured_body(featured: Option<&InstalledPlugin>) -> AnyElement { + let (name, desc) = match featured { + Some(plugin) => ( + plugin.manifest().name().to_string(), + plugin.manifest().description().to_string(), + ), + None => ( + "Install your first plugin".to_string(), + "Drop a signed .rplug package onto ELY to extend the browser with sandboxed tools." + .to_string(), + ), + }; + + div() + .flex() + .gap(px(14.0)) + .child( + div() + .size(px(56.0)) + .rounded(px(14.0)) + .bg(linear_gradient( + 135.0, + linear_color_stop(hsla(341.0 / 360.0, 0.78, 0.85, 1.0), 0.0), + linear_color_stop(hsla(228.0 / 360.0, 1.0, 0.86, 1.0), 1.0), + )) + .flex() + .items_center() + .justify_center() + .text_size(px(28.0)) + .text_color(rgb(0xffffff)) + .child(name.chars().next().unwrap_or('✦').to_uppercase().to_string()), + ) + .child( + div() + .flex_1() + .min_w_0() + .flex() + .flex_col() + .gap_1() + .child( + div() + .text_size(px(15.0)) + .font_weight(FontWeight(600.0)) + .text_color(rgb(colors::INK)) + .child(name), + ) + .child( + div() + .text_size(px(12.5)) + .text_color(rgb(colors::INK_3)) + .child(desc), + ), + ) + .into_any_element() +} + +fn render_featured_actions( + featured: Option<&InstalledPlugin>, + cx: &mut Context, +) -> AnyElement { + if let Some(plugin) = featured { + let detail_route = format!("ely://plugin/{}", plugin.id().as_str()); + + div() + .flex() + .items_center() + .gap(px(8.0)) + .child(action_button( + "featured-open", + "Open detail", + cx, + move |shell, window, cx| { + shell.open_internal_tab(&detail_route, window, cx); + }, + )) + .child( + div() + .ml_auto() + .text_size(px(11.5)) + .text_color(rgb(colors::INK_3)) + .child(format!( + "{} permissions", + plugin.manifest().permissions().len() + )), + ) + .into_any_element() + } else { + div() + .flex() + .items_center() + .gap(px(8.0)) + .child(action_button( + "featured-install", + "Install", + cx, + |shell, window, cx| shell.choose_plugin_package(window, cx), + )) + .into_any_element() + } +} + +pub(super) fn action_button( + id: &'static str, + label: &'static str, + cx: &mut Context, + handler: F, +) -> AnyElement +where + F: Fn(&mut ElyShell, &mut gpui::Window, &mut Context) + 'static, +{ + div() + .id(SharedString::from(id)) + .px(px(14.0)) + .py(px(7.0)) + .rounded(px(8.0)) + .bg(rgb(colors::INK)) + .text_size(px(12.0)) + .font_weight(FontWeight(500.0)) + .text_color(rgb(0xffffff)) + .cursor_pointer() + .hover(|style| style.opacity(0.92)) + .active(|style| style.opacity(0.78)) + .on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx))) + .child(label) + .into_any_element() +} + +pub(super) fn featured_plugin(snapshot: &BrowserSnapshot) -> Option<&InstalledPlugin> { + snapshot + .installed_plugins + .iter() + .find(|plugin| plugin.enabled_for_profile(&snapshot.active_profile_kind)) + .or_else(|| snapshot.installed_plugins.first()) +}