From 9c5f93bcb8e27a4a09e5bbf5ed65a977482fffdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 7 May 2026 20:54:57 -0400 Subject: [PATCH] Open downloads from command bar --- crates/ely_app/src/main.rs | 5 +++++ crates/ely_app/src/shell/mod.rs | 22 ++++++++++++++++++- crates/ely_app/src/shell/render.rs | 1 + crates/ely_browser_core/src/navigation.rs | 16 ++++++++++++-- crates/ely_browser_core/src/state/commands.rs | 8 +++++-- crates/ely_browser_core/tests/commands.rs | 17 ++++++++++++++ 6 files changed, 64 insertions(+), 5 deletions(-) diff --git a/crates/ely_app/src/main.rs b/crates/ely_app/src/main.rs index b96849c..3ac3f09 100644 --- a/crates/ely_app/src/main.rs +++ b/crates/ely_app/src/main.rs @@ -12,6 +12,7 @@ actions!( [ CloseCurrentTab, FocusAddressBar, + OpenDownloads, OpenNewTab, Quit, RestoreClosedTab, @@ -29,6 +30,8 @@ fn main() { cx.bind_keys([ KeyBinding::new("cmd-t", OpenNewTab, None), KeyBinding::new("ctrl-t", OpenNewTab, None), + KeyBinding::new("cmd-shift-j", OpenDownloads, None), + KeyBinding::new("ctrl-shift-j", OpenDownloads, None), KeyBinding::new("cmd-l", FocusAddressBar, None), KeyBinding::new("ctrl-l", FocusAddressBar, None), KeyBinding::new("cmd-w", CloseCurrentTab, None), @@ -63,6 +66,8 @@ fn main() { MenuItem::separator(), MenuItem::action("Restore Closed Tab", RestoreClosedTab), MenuItem::separator(), + MenuItem::action("Open Downloads", OpenDownloads), + MenuItem::separator(), MenuItem::action("Toggle Pin", TogglePinnedTab), ], }, diff --git a/crates/ely_app/src/shell/mod.rs b/crates/ely_app/src/shell/mod.rs index 2b48000..cee03ff 100644 --- a/crates/ely_app/src/shell/mod.rs +++ b/crates/ely_app/src/shell/mod.rs @@ -6,7 +6,7 @@ use gpui::{App, AppContext, Context, Entity, FocusHandle, Focusable, Subscriptio use gpui_component::input::{InputEvent, InputState, SelectAll}; use crate::{ - CloseCurrentTab, FocusAddressBar, OpenNewTab, RestoreClosedTab, SelectNextTab, + CloseCurrentTab, FocusAddressBar, OpenDownloads, OpenNewTab, RestoreClosedTab, SelectNextTab, SelectPreviousTab, ToggleFavoriteTab, TogglePinnedTab, }; @@ -90,6 +90,17 @@ impl ElyShell { } } + fn open_downloads(&mut self, window: &mut Window, cx: &mut Context) { + if let ShellState::Ready(core) = &mut self.state + && let Ok(url) = UrlText::parse("ely://downloads") + { + core.open_tab(url); + self.sync_address_input(window, cx); + self.focus_address_bar(window, cx); + cx.notify(); + } + } + fn focus_address_bar(&mut self, window: &mut Window, cx: &mut Context) { self.command_input.update(cx, |input, cx| { input.focus(window, cx); @@ -203,6 +214,15 @@ impl ElyShell { self.open_new_tab(window, cx); } + fn on_open_downloads( + &mut self, + _: &OpenDownloads, + window: &mut Window, + cx: &mut Context, + ) { + self.open_downloads(window, cx); + } + fn on_restore_closed_tab( &mut self, _: &RestoreClosedTab, diff --git a/crates/ely_app/src/shell/render.rs b/crates/ely_app/src/shell/render.rs index 8f571b0..866dc0c 100644 --- a/crates/ely_app/src/shell/render.rs +++ b/crates/ely_app/src/shell/render.rs @@ -37,6 +37,7 @@ impl ElyShell { .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_close_current_tab)) .on_action(cx.listener(Self::on_focus_address_bar)) + .on_action(cx.listener(Self::on_open_downloads)) .on_action(cx.listener(Self::on_open_new_tab)) .on_action(cx.listener(Self::on_restore_closed_tab)) .on_action(cx.listener(Self::on_select_next_tab)) diff --git a/crates/ely_browser_core/src/navigation.rs b/crates/ely_browser_core/src/navigation.rs index 1c9904a..8e91184 100644 --- a/crates/ely_browser_core/src/navigation.rs +++ b/crates/ely_browser_core/src/navigation.rs @@ -6,13 +6,21 @@ use crate::CoreError; const DEFAULT_SEARCH_URL: &str = "https://duckduckgo.com/"; pub(crate) fn tab_title(url: &UrlText) -> String { - if url.as_str() == "ely://new-tab" { - return "New Tab".to_string(); + if let Some(title) = internal_page_title(url.as_str()) { + return title.to_string(); } url.display_host() } +fn internal_page_title(url: &str) -> Option<&'static str> { + match url { + "ely://new-tab" => Some("New Tab"), + "ely://downloads" => Some("Downloads"), + _ => None, + } +} + pub(crate) fn tab_matches_query(tab: &BrowserTab, normalized_query: &str) -> bool { tab.title().to_lowercase().contains(normalized_query) || tab.url().as_str().to_lowercase().contains(normalized_query) @@ -59,3 +67,7 @@ pub(crate) fn search_url(query: &str) -> Result { url.query_pairs_mut().append_pair("q", query); UrlText::parse(url.to_string()).map_err(CoreError::from) } + +pub(crate) fn downloads_url() -> Result { + UrlText::parse("ely://downloads").map_err(CoreError::from) +} diff --git a/crates/ely_browser_core/src/state/commands.rs b/crates/ely_browser_core/src/state/commands.rs index 57e30d9..e1f1e3a 100644 --- a/crates/ely_browser_core/src/state/commands.rs +++ b/crates/ely_browser_core/src/state/commands.rs @@ -3,8 +3,8 @@ use ely_domain::{CommandIntent, CommandScope, ProfileId, ProfileKind, SpaceId}; use crate::{ CoreError, navigation::{ - move_tab_space_name, new_profile_name, new_space_name, search_url, space_icon, - switch_profile_name, + downloads_url, move_tab_space_name, new_profile_name, new_space_name, search_url, + space_icon, switch_profile_name, }, }; @@ -85,6 +85,10 @@ impl BrowserCore { self.open_tab(self.new_tab_url.clone()); Ok(true) } + "downloads" | "open-downloads" | "open downloads" => { + self.open_tab(downloads_url()?); + Ok(true) + } "close-tab" => { self.close_active_tab()?; Ok(true) diff --git a/crates/ely_browser_core/tests/commands.rs b/crates/ely_browser_core/tests/commands.rs index 13fd627..c15d0b1 100644 --- a/crates/ely_browser_core/tests/commands.rs +++ b/crates/ely_browser_core/tests/commands.rs @@ -49,6 +49,23 @@ fn new_tab_command_opens_new_tab() -> Result<(), Box> { Ok(()) } +#[test] +fn open_downloads_command_opens_downloads_page() -> Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + + core.set_command_query(">open-downloads"); + let intent = core.submit_command()?; + let snapshot = core.snapshot()?; + let active_tab = core.active_tab()?; + + assert_eq!(intent, Some(CommandIntent::Command("open-downloads".to_string()))); + assert_eq!(snapshot.tabs.len(), 2); + assert_eq!(active_tab.title(), "Downloads"); + assert_eq!(active_tab.url().as_str(), "ely://downloads"); + assert_eq!(snapshot.command_query, ""); + Ok(()) +} + #[test] fn new_space_command_creates_and_selects_named_space() -> Result<(), Box> { let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;