acp: Add agent websites to the registry page (#52002)

Ben Brandt created

## Context

The registry now distinguishes between websites and repos, so we can
show either or both if available.

## Self-Review Checklist

<!-- Check before requesting review: -->
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A

Change summary

crates/agent_servers/src/custom.rs         |  1 +
crates/agent_ui/src/agent_registry_ui.rs   | 19 ++++++++++++++++++-
crates/project/src/agent_registry_store.rs |  8 ++++++++
3 files changed, 27 insertions(+), 1 deletion(-)

Detailed changes

crates/agent_servers/src/custom.rs 🔗

@@ -484,6 +484,7 @@ mod tests {
                         description: SharedString::from(""),
                         version: SharedString::from("1.0.0"),
                         repository: None,
+                        website: None,
                         icon_path: None,
                     },
                     package: id,

crates/agent_ui/src/agent_registry_ui.rs 🔗

@@ -403,6 +403,22 @@ impl AgentRegistryPage {
             })
         });
 
+        let website_button = agent.website().map(|website| {
+            let website = website.clone();
+            let website_for_click = website.clone();
+            IconButton::new(
+                SharedString::from(format!("agent-website-{}", agent.id())),
+                IconName::Link,
+            )
+            .icon_size(IconSize::Small)
+            .tooltip(move |_, cx| {
+                Tooltip::with_meta("Visit Agent Website", None, website.clone(), cx)
+            })
+            .on_click(move |_, _, cx| {
+                cx.open_url(&website_for_click);
+            })
+        });
+
         AgentRegistryCard::new()
             .child(
                 h_flex()
@@ -441,7 +457,8 @@ impl AgentRegistryPage {
                                     .color(Color::Muted)
                                     .truncate(),
                             )
-                            .when_some(repository_button, |this, button| this.child(button)),
+                            .when_some(repository_button, |this, button| this.child(button))
+                            .when_some(website_button, |this, button| this.child(button)),
                     ),
             )
     }

crates/project/src/agent_registry_store.rs 🔗

@@ -23,6 +23,7 @@ pub struct RegistryAgentMetadata {
     pub description: SharedString,
     pub version: SharedString,
     pub repository: Option<SharedString>,
+    pub website: Option<SharedString>,
     pub icon_path: Option<SharedString>,
 }
 
@@ -75,6 +76,10 @@ impl RegistryAgent {
         self.metadata().repository.as_ref()
     }
 
+    pub fn website(&self) -> Option<&SharedString> {
+        self.metadata().website.as_ref()
+    }
+
     pub fn icon_path(&self) -> Option<&SharedString> {
         self.metadata().icon_path.as_ref()
     }
@@ -369,6 +374,7 @@ async fn build_registry_agents(
             description: entry.description.into(),
             version: entry.version.into(),
             repository: entry.repository.map(Into::into),
+            website: entry.website.map(Into::into),
             icon_path,
         };
 
@@ -568,6 +574,8 @@ struct RegistryEntry {
     #[serde(default)]
     repository: Option<String>,
     #[serde(default)]
+    website: Option<String>,
+    #[serde(default)]
     icon: Option<String>,
     distribution: RegistryDistribution,
 }