Wire plugin file install command
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
use ely_domain::CommandIntent;
|
||||||
|
use gpui::{Context, Window};
|
||||||
|
|
||||||
|
use super::ElyShell;
|
||||||
|
|
||||||
|
impl ElyShell {
|
||||||
|
pub(super) fn handle_shell_command_intent(
|
||||||
|
&mut self,
|
||||||
|
intent: Option<&CommandIntent>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
let Some(CommandIntent::Command(command)) = intent else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if install_plugin_from_file_command(command) {
|
||||||
|
self.choose_plugin_package(window, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_plugin_from_file_command(command: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
command.trim().to_ascii_lowercase().as_str(),
|
||||||
|
"install-plugin-from-file"
|
||||||
|
| "install plugin from file"
|
||||||
|
| "install-plugin"
|
||||||
|
| "install plugin"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::install_plugin_from_file_command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn install_plugin_from_file_command_matches_prd_aliases() {
|
||||||
|
assert!(install_plugin_from_file_command("install-plugin-from-file"));
|
||||||
|
assert!(install_plugin_from_file_command("Install Plugin from File"));
|
||||||
|
assert!(install_plugin_from_file_command("install plugin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn install_plugin_from_file_command_rejects_other_plugin_commands() {
|
||||||
|
assert!(!install_plugin_from_file_command("plugins"));
|
||||||
|
assert!(!install_plugin_from_file_command("open plugins"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
mod archive_labels;
|
mod archive_labels;
|
||||||
mod bookmarks;
|
mod bookmarks;
|
||||||
|
mod command_actions;
|
||||||
mod downloads;
|
mod downloads;
|
||||||
mod focus;
|
mod focus;
|
||||||
mod history;
|
mod history;
|
||||||
@@ -25,9 +26,9 @@ mod web_surface_view;
|
|||||||
|
|
||||||
use ely_browser_core::{BrowserCore, InitialBrowserConfig};
|
use ely_browser_core::{BrowserCore, InitialBrowserConfig};
|
||||||
use ely_domain::{
|
use ely_domain::{
|
||||||
ArchivePolicy, CommandIntent, DownloadPolicy, FavoriteLimit, HistoryRecordingPolicy,
|
ArchivePolicy, DownloadPolicy, FavoriteLimit, HistoryRecordingPolicy, NewTabDestination,
|
||||||
NewTabDestination, ProfileId, ProfileSyncPolicy, SearchEngine, SpaceId, SyncObjectKind,
|
ProfileId, ProfileSyncPolicy, SearchEngine, SpaceId, SyncObjectKind, SyncObjectPolicy, TabId,
|
||||||
SyncObjectPolicy, TabId, UrlText,
|
UrlText,
|
||||||
};
|
};
|
||||||
use gpui::{AppContext, Context, Entity, FocusHandle, Subscription, Window};
|
use gpui::{AppContext, Context, Entity, FocusHandle, Subscription, Window};
|
||||||
use gpui_component::input::{InputEvent, InputState, SelectAll};
|
use gpui_component::input::{InputEvent, InputState, SelectAll};
|
||||||
@@ -53,7 +54,6 @@ pub struct ElyShell {
|
|||||||
state: ShellState,
|
state: ShellState,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
command_input: Entity<InputState>,
|
command_input: Entity<InputState>,
|
||||||
last_intent: Option<CommandIntent>,
|
|
||||||
download_action_error: Option<String>,
|
download_action_error: Option<String>,
|
||||||
download_clear_confirmation: bool,
|
download_clear_confirmation: bool,
|
||||||
download_security_confirmation: Option<PendingDownloadFileAction>,
|
download_security_confirmation: Option<PendingDownloadFileAction>,
|
||||||
@@ -84,6 +84,7 @@ impl ElyShell {
|
|||||||
let mut sync_address = false;
|
let mut sync_address = false;
|
||||||
let submitted = matches!(event, InputEvent::PressEnter { .. });
|
let submitted = matches!(event, InputEvent::PressEnter { .. });
|
||||||
|
|
||||||
|
{
|
||||||
let ShellState::Ready(core) = &mut shell.state else {
|
let ShellState::Ready(core) = &mut shell.state else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -95,11 +96,10 @@ impl ElyShell {
|
|||||||
submitted_intent = core.submit_command().ok().flatten();
|
submitted_intent = core.submit_command().ok().flatten();
|
||||||
sync_address = core.command_query().is_empty();
|
sync_address = core.command_query().is_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if submitted {
|
|
||||||
shell.last_intent = submitted_intent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shell.handle_shell_command_intent(submitted_intent.as_ref(), window, cx);
|
||||||
|
|
||||||
if sync_address {
|
if sync_address {
|
||||||
shell.sync_address_input(window, cx);
|
shell.sync_address_input(window, cx);
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,6 @@ impl ElyShell {
|
|||||||
state,
|
state,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
command_input,
|
command_input,
|
||||||
last_intent: None,
|
|
||||||
download_action_error: None,
|
download_action_error: None,
|
||||||
download_clear_confirmation: false,
|
download_clear_confirmation: false,
|
||||||
download_security_confirmation: None,
|
download_security_confirmation: None,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use crate::services::{
|
|||||||
|
|
||||||
use super::{ElyShell, ShellState};
|
use super::{ElyShell, ShellState};
|
||||||
|
|
||||||
|
const PLUGIN_SETTINGS_URL: &str = "ely://settings/plugins";
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(super) struct PendingPluginInstall {
|
pub(super) struct PendingPluginInstall {
|
||||||
package: VerifiedPluginPackage,
|
package: VerifiedPluginPackage,
|
||||||
@@ -56,6 +58,8 @@ impl PendingPluginUninstall {
|
|||||||
|
|
||||||
impl ElyShell {
|
impl ElyShell {
|
||||||
pub(super) fn choose_plugin_package(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
pub(super) fn choose_plugin_package(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.ensure_plugin_install_surface(window, cx);
|
||||||
|
|
||||||
let prompt = cx.prompt_for_paths(PathPromptOptions {
|
let prompt = cx.prompt_for_paths(PathPromptOptions {
|
||||||
files: false,
|
files: false,
|
||||||
directories: true,
|
directories: true,
|
||||||
@@ -96,6 +100,21 @@ impl ElyShell {
|
|||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_plugin_install_surface(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
if self.active_tab_matches_url(PLUGIN_SETTINGS_URL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.open_internal_tab(PLUGIN_SETTINGS_URL, window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_tab_matches_url(&self, url: &str) -> bool {
|
||||||
|
match &self.state {
|
||||||
|
ShellState::Ready(core) => core.active_tab().is_ok_and(|tab| tab.url().as_str() == url),
|
||||||
|
ShellState::StartupError(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn confirm_plugin_install(&mut self, cx: &mut Context<Self>) {
|
pub(super) fn confirm_plugin_install(&mut self, cx: &mut Context<Self>) {
|
||||||
let Some(pending) = self.pending_plugin_install.take() else {
|
let Some(pending) = self.pending_plugin_install.take() else {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|||||||
@@ -202,6 +202,10 @@ pub(crate) fn plugins_url() -> Result<UrlText, CoreError> {
|
|||||||
internal_page_url("ely://plugins")
|
internal_page_url("ely://plugins")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn plugin_settings_url() -> Result<UrlText, CoreError> {
|
||||||
|
internal_page_url("ely://settings/plugins")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn plugin_detail_url(plugin_id: &PluginId) -> Result<UrlText, CoreError> {
|
pub(crate) fn plugin_detail_url(plugin_id: &PluginId) -> Result<UrlText, CoreError> {
|
||||||
let route = format!("ely://plugin/{}", plugin_id.as_str());
|
let route = format!("ely://plugin/{}", plugin_id.as_str());
|
||||||
internal_page_url(&route)
|
internal_page_url(&route)
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ use crate::{
|
|||||||
navigation::{
|
navigation::{
|
||||||
about_url, archive_idle_days, archive_url, bookmarks_url, downloads_url, history_url,
|
about_url, archive_idle_days, archive_url, bookmarks_url, downloads_url, history_url,
|
||||||
move_tab_space_name, new_private_profile_name, new_profile_name, new_space_name, note_body,
|
move_tab_space_name, new_private_profile_name, new_profile_name, new_space_name, note_body,
|
||||||
notes_url, plugin_detail_url, plugins_url, reading_list_url, reading_progress_percent,
|
notes_url, plugin_detail_url, plugin_settings_url, plugins_url, reading_list_url,
|
||||||
rename_tab_group_name, search_url, settings_page_url, settings_url, shortcut_settings_url,
|
reading_progress_percent, rename_tab_group_name, search_url, settings_page_url,
|
||||||
space_icon, split_group_name, switch_profile_name, sync_status_url, tab_group_color_hex,
|
settings_url, shortcut_settings_url, space_icon, split_group_name, switch_profile_name,
|
||||||
tab_group_name, tab_note_body, task_manager_url,
|
sync_status_url, tab_group_color_hex, tab_group_name, tab_note_body, task_manager_url,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -271,6 +271,13 @@ impl BrowserCore {
|
|||||||
self.open_tab(plugins_url()?);
|
self.open_tab(plugins_url()?);
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
"install-plugin-from-file"
|
||||||
|
| "install plugin from file"
|
||||||
|
| "install-plugin"
|
||||||
|
| "install plugin" => {
|
||||||
|
self.open_tab(plugin_settings_url()?);
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
"site-settings" | "open-site-settings" | "open site settings" => {
|
"site-settings" | "open-site-settings" | "open site settings" => {
|
||||||
let Some(url) = self.active_tab_site_settings_url()? else {
|
let Some(url) = self.active_tab_site_settings_url()? else {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|||||||
@@ -3,6 +3,21 @@ use std::{error::Error, io};
|
|||||||
use ely_browser_core::{BrowserCore, CoreError, InitialBrowserConfig, PluginAuditAction};
|
use ely_browser_core::{BrowserCore, CoreError, InitialBrowserConfig, PluginAuditAction};
|
||||||
use ely_domain::{CommandIntent, CommandScope, PluginId, PluginManifest};
|
use ely_domain::{CommandIntent, CommandScope, PluginId, PluginManifest};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn install_plugin_from_file_command_opens_plugin_settings_page() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
|
||||||
|
core.set_command_query(">install-plugin-from-file");
|
||||||
|
let intent = core.submit_command()?;
|
||||||
|
let active_tab = core.active_tab()?;
|
||||||
|
|
||||||
|
assert_eq!(intent, Some(CommandIntent::Command("install-plugin-from-file".to_string())));
|
||||||
|
assert_eq!(active_tab.title(), "Plugin Settings");
|
||||||
|
assert_eq!(active_tab.url().as_str(), "ely://settings/plugins");
|
||||||
|
assert_eq!(core.snapshot()?.command_query, "");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn installs_standard_plugin_and_records_audit_event() -> Result<(), Box<dyn Error>> {
|
fn installs_standard_plugin_and_records_audit_event() -> Result<(), Box<dyn Error>> {
|
||||||
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user