From 8f0161b3c22e893d7aa3b7cdb655fee333b2fbf6 Mon Sep 17 00:00:00 2001 From: Albert Bogusz Date: Tue, 4 Aug 2026 17:14:33 +0200 Subject: [PATCH] Use correct provider icons for extension repository links (#58108) Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments (N/A) - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable I was unable to find an issue or PR which mentions this. Extensions with non-GitHub repository links always use the GitHub icon, which is inconsistent with the changes merged in #44738 and #57500. This change makes `extension_ui.rs` use the Git hosting provider registry to determine these icons. Below is a preview of the change:
Preview
Release Notes: - Improved extension repository links to show provider-specific icons. --- Cargo.lock | 1 + crates/extensions_ui/Cargo.toml | 1 + .../src/components/extension_card.rs | 11 ++++++++- crates/extensions_ui/src/extensions_ui.rs | 24 +++++++++++++++++-- crates/git_ui/src/branch_picker.rs | 2 +- crates/git_ui/src/commit_view.rs | 2 +- crates/git_ui/src/git_graph.rs | 2 +- crates/git_ui/src/git_ui.rs | 14 ----------- crates/ui/src/components/icon.rs | 14 +++++++++++ 9 files changed, 51 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0f2b8cfb8..631caab9e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6439,6 +6439,7 @@ dependencies = [ "extension_host", "fs", "fuzzy", + "git", "gpui", "language", "log", diff --git a/crates/extensions_ui/Cargo.toml b/crates/extensions_ui/Cargo.toml index 617d53e0cd..4729bf9ca6 100644 --- a/crates/extensions_ui/Cargo.toml +++ b/crates/extensions_ui/Cargo.toml @@ -23,6 +23,7 @@ extension.workspace = true extension_host.workspace = true fs.workspace = true fuzzy.workspace = true +git.workspace = true gpui.workspace = true language.workspace = true log.workspace = true diff --git a/crates/extensions_ui/src/components/extension_card.rs b/crates/extensions_ui/src/components/extension_card.rs index 22095a5d8d..feb6251c2c 100644 --- a/crates/extensions_ui/src/components/extension_card.rs +++ b/crates/extensions_ui/src/components/extension_card.rs @@ -68,6 +68,7 @@ struct ExtensionCardDetails { description: Option, authors: SharedString, repository_url: Option, + repository_icon: IconName, provided_features: Vec<&'static str>, source: ExtensionCardSource, } @@ -145,6 +146,7 @@ impl ExtensionCard { description: extension.description.clone().map(Into::into), authors: extension.authors.join(", ").into(), repository_url: extension.repository.clone().map(Into::into), + repository_icon: IconName::Link, provided_features: provided_feature_labels(extension.provides()), source: ExtensionCardSource::Dev, }; @@ -169,6 +171,7 @@ impl ExtensionCard { description: extension.manifest.description.clone().map(Into::into), authors: extension.manifest.authors.join(", ").into(), repository_url: Some(extension.manifest.repository.clone().into()), + repository_icon: IconName::Link, provided_features: provided_feature_labels(extension.manifest.provides.iter().copied()), source: ExtensionCardSource::Remote { status, @@ -393,6 +396,11 @@ impl ExtensionCard { } } + pub fn repository_icon(mut self, icon: IconName) -> Self { + self.details.repository_icon = icon; + self + } + pub fn context_menu( mut self, builder: impl Fn(Arc, SharedString, &mut Window, &mut App) -> Option> @@ -601,6 +609,7 @@ impl RenderOnce for ExtensionCard { description, authors, repository_url, + repository_icon, provided_features, source, } = details; @@ -701,7 +710,7 @@ impl RenderOnce for ExtensionCard { .when_some(repository_url, |this, repository_url| { let repository_url_for_tooltip = repository_url.clone(); this.child( - IconButton::new(repository_button_id, IconName::Github) + IconButton::new(repository_button_id, repository_icon) .icon_size(IconSize::Small) .tooltip(move |_, cx| { Tooltip::with_meta( diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 1a0f77cfde..674f5d56bc 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -13,6 +13,7 @@ use command_palette_hooks::CommandPaletteFilter; use editor::{Editor, EditorElement, EditorStyle}; use extension_host::{ExtensionManifest, ExtensionStore}; use fuzzy::{StringMatch, StringMatchCandidate, match_strings}; +use git::{GitHostingProviderRegistry, parse_git_remote_url}; use gpui::{ Action, App, ClipboardItem, Context, DismissEvent, Entity, EventEmitter, Focusable, InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TaskExt, TextStyle, @@ -378,6 +379,7 @@ fn keywords_by_feature() -> &'static BTreeMap> { pub struct ExtensionsPage { workspace: WeakEntity, + provider_registry: Arc, list: UniformListScrollHandle, is_fetching_extensions: bool, fetch_failed: bool, @@ -437,9 +439,11 @@ impl ExtensionsPage { cx.subscribe(&query_editor, Self::on_query_change).detach(); let scroll_handle = UniformListScrollHandle::new(); + let provider_registry = GitHostingProviderRegistry::default_global(cx); let mut this = Self { workspace: workspace.weak_handle(), + provider_registry, list: scroll_handle, is_fetching_extensions: false, fetch_failed: false, @@ -465,6 +469,12 @@ impl ExtensionsPage { }) } + fn get_repository_icon(&self, repository_url: &str) -> IconName { + parse_git_remote_url(Arc::clone(&self.provider_registry), repository_url) + .map(|(provider, _)| ui::git_hosting_provider_icon(provider.name().as_str())) + .unwrap_or(IconName::Link) + } + fn on_extension_installed( &mut self, workspace: WeakEntity, @@ -665,7 +675,16 @@ impl ExtensionsPage { if ix < dev_extension_entries_len { let dev_ix = self.filtered_dev_extension_indices[ix]; let extension = &self.dev_extension_entries[dev_ix]; - ExtensionCard::for_dev(extension.clone(), cx) + let repository_icon = extension + .repository + .as_deref() + .map(|url| self.get_repository_icon(url)); + let card = ExtensionCard::for_dev(extension.clone(), cx); + if let Some(icon) = repository_icon { + card.repository_icon(icon) + } else { + card + } } else { let extension_ix = self.filtered_remote_extension_indices[ix - dev_extension_entries_len]; @@ -681,7 +700,8 @@ impl ExtensionsPage { extension: &ExtensionMetadata, cx: &mut Context, ) -> ExtensionCard { - let card = ExtensionCard::for_remote(extension, cx); + let repository_icon = self.get_repository_icon(&extension.manifest.repository); + let card = ExtensionCard::for_remote(extension, cx).repository_icon(repository_icon); let this = cx.weak_entity(); card.context_menu(move |extension_id, authors, window, cx| { diff --git a/crates/git_ui/src/branch_picker.rs b/crates/git_ui/src/branch_picker.rs index 942cf0f660..604d46b9be 100644 --- a/crates/git_ui/src/branch_picker.rs +++ b/crates/git_ui/src/branch_picker.rs @@ -1226,7 +1226,7 @@ fn remote_provider_icons( let (provider, _) = parse_git_remote_url(provider_registry.clone(), remote_url)?; Some(( remote_name.clone(), - crate::get_provider_icon(&provider.name()), + ui::git_hosting_provider_icon(provider.name().as_str()), )) }) .collect() diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index 31f3937192..5856285831 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -1344,7 +1344,7 @@ impl Render for CommitViewToolbar { }), ) .children(remote_info.map(|(provider_name, url)| { - let icon = crate::get_provider_icon(provider_name.as_str()); + let icon = ui::git_hosting_provider_icon(provider_name.as_str()); IconButton::new("view_on_provider", icon) .icon_size(IconSize::Small) diff --git a/crates/git_ui/src/git_graph.rs b/crates/git_ui/src/git_graph.rs index ffc9439190..d010200350 100644 --- a/crates/git_ui/src/git_graph.rs +++ b/crates/git_ui/src/git_graph.rs @@ -2982,7 +2982,7 @@ impl GitGraph { }) .when_some(remote.clone(), |this, remote| { let provider_name = remote.host.name(); - let icon = crate::get_provider_icon(provider_name.as_str()); + let icon = ui::git_hosting_provider_icon(provider_name.as_str()); let parsed_remote = ParsedGitRemote { owner: remote.owner.as_ref().into(), repo: remote.repo.as_ref().into(), diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index d9d0d3370b..25ceb9f350 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -60,20 +60,6 @@ pub mod unstaged_diff; pub use blame_ui::GitBlameStatus; pub use conflict_view::MergeConflictIndicator; -pub fn get_provider_icon(name: &str) -> IconName { - match name { - "Bitbucket" => IconName::Bitbucket, - "Chromium" => IconName::Gerrit, - "Codeberg" => IconName::Codeberg, - "Forgejo Self-Hosted" => IconName::Forgejo, - "GitHub" => IconName::Github, - "GitLab" => IconName::Gitlab, - "Gitea" => IconName::Gitea, - "SourceHut" => IconName::Sourcehut, - _ => IconName::Link, - } -} - pub fn init(cx: &mut App) { editor::set_blame_renderer(blame_ui::GitBlameRenderer, cx); commit_view::init(cx); diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 6089bba4b8..f1d4554b63 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -112,6 +112,20 @@ impl From for Icon { } } +pub fn git_hosting_provider_icon(provider_name: &str) -> IconName { + match provider_name { + "Bitbucket" => IconName::Bitbucket, + "Chromium" => IconName::Gerrit, + "Codeberg" => IconName::Codeberg, + "Forgejo Self-Hosted" => IconName::Forgejo, + "GitHub" => IconName::Github, + "GitLab" => IconName::Gitlab, + "Gitea" => IconName::Gitea, + "SourceHut" => IconName::Sourcehut, + _ => IconName::Link, + } +} + /// The source of an icon. #[derive(Clone)] enum IconSource {