Add "Copy Extension ID" action to extension card dropdown (#21395)

uncenter and Marshall Bowers created

Adds a new "Copy Extension ID" action to the dropdown of remote
extension cards in the extensions list UI. Would have liked for it to be
a context menu where you could click anywhere on the card, but couldn't
figure out how to integrate that with the existing setup.

I've been missing this from VSCode's extension panel, which allows this
on right click:

![CleanShot 2024-12-01 at 22 03
14](https://github.com/user-attachments/assets/64796f96-1a37-4ba2-bfe1-971b939aa50a)

This is useful if you, say, want to add some extensions to
https://zed.dev/docs/configuring-zed#auto-install-extensions, where you
need the IDs.

Release Notes:

- Added "Copy Extension ID" action to extension card dropdown

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>

Change summary

crates/extensions_ui/src/extensions_ui.rs | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)

Detailed changes

crates/extensions_ui/src/extensions_ui.rs 🔗

@@ -14,7 +14,7 @@ use editor::{Editor, EditorElement, EditorStyle};
 use extension_host::{ExtensionManifest, ExtensionOperation, ExtensionStore};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, uniform_list, Action, AppContext, EventEmitter, Flatten, FocusableView,
+    actions, uniform_list, Action, AppContext, ClipboardItem, EventEmitter, Flatten, FocusableView,
     InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
     UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
@@ -637,13 +637,21 @@ impl ExtensionsPage {
         cx: &mut WindowContext,
     ) -> View<ContextMenu> {
         let context_menu = ContextMenu::build(cx, |context_menu, cx| {
-            context_menu.entry(
-                "Install Another Version...",
-                None,
-                cx.handler_for(this, move |this, cx| {
-                    this.show_extension_version_list(extension_id.clone(), cx)
-                }),
-            )
+            context_menu
+                .entry(
+                    "Install Another Version...",
+                    None,
+                    cx.handler_for(this, {
+                        let extension_id = extension_id.clone();
+                        move |this, cx| this.show_extension_version_list(extension_id.clone(), cx)
+                    }),
+                )
+                .entry("Copy Extension ID", None, {
+                    let extension_id = extension_id.clone();
+                    move |cx| {
+                        cx.write_to_clipboard(ClipboardItem::new_string(extension_id.to_string()));
+                    }
+                })
         });
 
         context_menu