agent: Copy text as Markdown (#28272)

Marshall Bowers and Antonio Scandurra created

Release Notes:

- agent: Copying text in the Agent Panel will now copy it as Markdown.

Co-authored-by: Antonio Scandurra <me@as-cii.com>

Change summary

assets/keymaps/default-linux.json |  7 +++++++
assets/keymaps/default-macos.json |  7 +++++++
crates/markdown/src/markdown.rs   | 20 ++++++++++++++++++--
3 files changed, 32 insertions(+), 2 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -634,6 +634,13 @@
       "ctrl-alt-e": "agent::RemoveAllContext"
     }
   },
+  {
+    "context": "AgentPanel > Markdown",
+    "bindings": {
+      "copy": "markdown::CopyAsMarkdown",
+      "ctrl-c": "markdown::CopyAsMarkdown"
+    }
+  },
   {
     "context": "AgentPanel && prompt_editor",
     "use_key_equivalents": true,

assets/keymaps/default-macos.json 🔗

@@ -291,6 +291,13 @@
       "cmd-alt-e": "agent::RemoveAllContext"
     }
   },
+  {
+    "context": "AgentPanel > Markdown",
+    "use_key_equivalents": true,
+    "bindings": {
+      "cmd-c": "markdown::CopyAsMarkdown"
+    }
+  },
   {
     "context": "AgentPanel && prompt_editor",
     "use_key_equivalents": true,

crates/markdown/src/markdown.rs 🔗

@@ -90,7 +90,7 @@ struct Options {
     copy_code_block_buttons: bool,
 }
 
-actions!(markdown, [Copy]);
+actions!(markdown, [Copy, CopyAsMarkdown]);
 
 impl Markdown {
     pub fn new(
@@ -178,6 +178,14 @@ impl Markdown {
         cx.write_to_clipboard(ClipboardItem::new_string(text));
     }
 
+    fn copy_as_markdown(&self, _: &mut Window, cx: &mut Context<Self>) {
+        if self.selection.end <= self.selection.start {
+            return;
+        }
+        let text = self.source[self.selection.start..self.selection.end].to_string();
+        cx.write_to_clipboard(ClipboardItem::new_string(text));
+    }
+
     fn parse(&mut self, cx: &mut Context<Self>) {
         if self.source.is_empty() {
             return;
@@ -1040,8 +1048,8 @@ impl Element for MarkdownElement {
         let mut context = KeyContext::default();
         context.add("Markdown");
         window.set_key_context(context);
-        let entity = self.markdown.clone();
         window.on_action(std::any::TypeId::of::<crate::Copy>(), {
+            let entity = self.markdown.clone();
             let text = rendered_markdown.text.clone();
             move |_, phase, window, cx| {
                 let text = text.clone();
@@ -1050,6 +1058,14 @@ impl Element for MarkdownElement {
                 }
             }
         });
+        window.on_action(std::any::TypeId::of::<crate::CopyAsMarkdown>(), {
+            let entity = self.markdown.clone();
+            move |_, phase, window, cx| {
+                if phase == DispatchPhase::Bubble {
+                    entity.update(cx, move |this, cx| this.copy_as_markdown(window, cx))
+                }
+            }
+        });
 
         self.paint_mouse_listeners(hitbox, &rendered_markdown.text, window, cx);
         rendered_markdown.element.paint(window, cx);