From c765a3c2d44f05d2261f6166edf80a2df435dbff Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:03:29 +0200 Subject: [PATCH] feat: github packages can now be opened in browser (#943) --- .../github_mods_can_be_viewed_in_browser.md | 5 ++ .../src/components/optional_mods_popup.rs | 1 + .../src/components/package_row.rs | 60 ++++++++++++------- .../view/app/cluster/package_manager/mod.rs | 2 + .../oneclient_content/src/bundles/types.rs | 14 +++++ 5 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 .changeset/github_mods_can_be_viewed_in_browser.md diff --git a/.changeset/github_mods_can_be_viewed_in_browser.md b/.changeset/github_mods_can_be_viewed_in_browser.md new file mode 100644 index 00000000..5fb0dacd --- /dev/null +++ b/.changeset/github_mods_can_be_viewed_in_browser.md @@ -0,0 +1,5 @@ +--- +default: minor +--- + +# GitHub hosted mods have a "View in browser" option that opens their repository diff --git a/packages/oneclient_app/src/components/optional_mods_popup.rs b/packages/oneclient_app/src/components/optional_mods_popup.rs index 22ac00ef..6aa080cb 100644 --- a/packages/oneclient_app/src/components/optional_mods_popup.rs +++ b/packages/oneclient_app/src/components/optional_mods_popup.rs @@ -106,6 +106,7 @@ fn entry_from_item( bundle_name: item.offer.as_ref().map(|(bundle, _)| bundle.clone()), provider: item.provider, github_hosted: item.github_hosted, + github_url: None, name: cached .map(|cached| cached.name.clone()) .filter(|name| !name.is_empty()) diff --git a/packages/oneclient_app/src/components/package_row.rs b/packages/oneclient_app/src/components/package_row.rs index e7da4bf3..ffbd51a2 100644 --- a/packages/oneclient_app/src/components/package_row.rs +++ b/packages/oneclient_app/src/components/package_row.rs @@ -46,6 +46,7 @@ pub struct PackageEntry { pub bundle_name: Option, pub provider: ProviderId, pub github_hosted: bool, + pub github_url: Option, pub name: String, pub file_name: String, pub author: String, @@ -322,6 +323,12 @@ pub fn package_context_menu( }); } + if let Some(url) = item.github_url.clone() { + menu = menu.action(IconType::LinkExternal01, "View in browser", move |()| { + crate::platform::open_url(&url); + }); + } + if let Some(hash) = item.hash.clone() { menu = menu.action( IconType::Folder, @@ -520,14 +527,27 @@ fn grid_meta( ) -> Element { let muted = CARD_NAME.with_a(scale_a(alpha, 0.5)); - let source = if item.github_hosted { + let source = if let Some(url) = item.github_url.clone() { + SourceLink { + text: "GitHub".to_string(), + on_press: EventHandler::new(move |()| crate::platform::open_url(&url)), + alpha, + } + .into_element() + } else if item.github_hosted { meta_text("GitHub".to_string(), muted) } else if item.is_remote() && navigable { + let provider = item.provider; + let package_id = item.package_id.clone(); SourceLink { - provider: item.provider, - package_id: item.package_id.clone(), - package_type, - cluster_id, + text: provider.to_string(), + on_press: EventHandler::new(move |()| { + let _ = RouterContext::get().push(Route::BrowserPackage { + cluster_id, + package_type: package_type.to_string(), + package_id: format!("{}:{}", provider as u8, package_id), + }); + }), alpha, } .into_element() @@ -565,10 +585,8 @@ fn grid_meta( #[derive(PartialEq)] struct SourceLink { - provider: ProviderId, - package_id: String, - package_type: &'static str, - cluster_id: i64, + text: String, + on_press: EventHandler<()>, alpha: u8, } @@ -585,10 +603,7 @@ impl Component for SourceLink { CARD_NAME.with_a(scale_a(self.alpha, 0.68)) }; - let provider = self.provider; - let package_id = self.package_id.clone(); - let package_type = self.package_type.to_string(); - let cluster_id = self.cluster_id; + let on_press = self.on_press.clone(); rect() .cursor(CursorIcon::Pointer) @@ -601,13 +616,9 @@ impl Component for SourceLink { .on_press(move |e: Event| { e.stop_propagation(); pressed.set(false); - let _ = RouterContext::get().push(Route::BrowserPackage { - cluster_id, - package_type: package_type.clone(), - package_id: format!("{}:{}", provider as u8, package_id), - }); + on_press.call(()); }) - .child(meta_text(provider.to_string(), color)) + .child(meta_text(self.text.clone(), color)) } } @@ -739,7 +750,16 @@ fn package_info( .max_width(Size::percent(60.)) .color(CARD_NAME), ) - .child(if item.github_hosted { + .child(if let Some(url) = item.github_url.clone() { + rect() + .cursor(CursorIcon::Pointer) + .on_press(move |e: Event| { + e.stop_propagation(); + crate::platform::open_url(&url); + }) + .child(github_badge()) + .into_element() + } else if item.github_hosted { github_badge() } else if remote { provider_badge(item.provider) diff --git a/packages/oneclient_app/src/view/app/cluster/package_manager/mod.rs b/packages/oneclient_app/src/view/app/cluster/package_manager/mod.rs index b46ba532..f3f28747 100644 --- a/packages/oneclient_app/src/view/app/cluster/package_manager/mod.rs +++ b/packages/oneclient_app/src/view/app/cluster/package_manager/mod.rs @@ -222,6 +222,7 @@ pub fn bundle_packages( opted_in, ); row.advanced = advanced; + row.github_url = file.github_repo_url(); rows.push(row); } } @@ -299,6 +300,7 @@ fn make_row( bundle_name, provider, github_hosted, + github_url: None, name, file_name, author, diff --git a/packages/oneclient_content/src/bundles/types.rs b/packages/oneclient_content/src/bundles/types.rs index ac01ae72..f099bbdf 100644 --- a/packages/oneclient_content/src/bundles/types.rs +++ b/packages/oneclient_content/src/bundles/types.rs @@ -73,6 +73,20 @@ impl BundleFile { }) } + pub fn github_repo_url(&self) -> Option { + let BundleFileKind::External { file, .. } = &self.kind else { + return None; + }; + let url = url::Url::parse(&file.url).ok()?; + if !matches!(url.host_str()?, "github.com" | "raw.githubusercontent.com") { + return None; + } + let mut segments = url.path_segments()?.filter(|s| !s.is_empty()); + let owner = segments.next()?; + let repo = segments.next()?; + Some(format!("https://github.com/{owner}/{repo}")) + } + pub fn display_name(&self) -> String { if let BundleFileKind::External { meta: Some(meta), ..