agent: Fix Open Thread as Markdown not working when another panel is focused (#29993)

Ben Brandt and Bennet Bo Fenner created

Release Notes:

- agent: Fix Open Thread as Markdown not working when another panel is
focused

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>

Change summary

crates/agent/src/active_thread.rs   | 62 +++++++++++++++++++++++++++++-
crates/agent/src/assistant_panel.rs | 44 +--------------------
2 files changed, 62 insertions(+), 44 deletions(-)

Detailed changes

crates/agent/src/active_thread.rs 🔗

@@ -1,3 +1,4 @@
+use crate::AssistantPanel;
 use crate::context::{AgentContextHandle, RULES_ICON};
 use crate::context_picker::{ContextPicker, MentionLink};
 use crate::context_store::ContextStore;
@@ -11,7 +12,6 @@ use crate::tool_use::{PendingToolUseStatus, ToolUse};
 use crate::ui::{
     AddedContext, AgentNotification, AgentNotificationEvent, AnimatedLabel, ContextPill,
 };
-use crate::{AssistantPanel, OpenActiveThreadAsMarkdown};
 use anyhow::Context as _;
 use assistant_settings::{AssistantSettings, NotifyWhenAgentWaiting};
 use assistant_tool::ToolUseStatus;
@@ -1786,8 +1786,15 @@ impl ActiveThread {
             .icon_size(IconSize::XSmall)
             .icon_color(Color::Ignored)
             .tooltip(Tooltip::text("Open Thread as Markdown"))
-            .on_click(|_, window, cx| {
-                window.dispatch_action(Box::new(OpenActiveThreadAsMarkdown), cx)
+            .on_click({
+                let thread = self.thread.clone();
+                let workspace = self.workspace.clone();
+                move |_, window, cx| {
+                    if let Some(workspace) = workspace.upgrade() {
+                        open_active_thread_as_markdown(thread.clone(), workspace, window, cx)
+                            .detach_and_log_err(cx);
+                    }
+                }
             });
 
         // For all items that should be aligned with the LLM's response.
@@ -3432,6 +3439,55 @@ impl Render for ActiveThread {
     }
 }
 
+pub(crate) fn open_active_thread_as_markdown(
+    thread: Entity<Thread>,
+    workspace: Entity<Workspace>,
+    window: &mut Window,
+    cx: &mut App,
+) -> Task<anyhow::Result<()>> {
+    let markdown_language_task = workspace
+        .read(cx)
+        .app_state()
+        .languages
+        .language_for_name("Markdown");
+
+    window.spawn(cx, async move |cx| {
+        let markdown_language = markdown_language_task.await?;
+
+        workspace.update_in(cx, |workspace, window, cx| {
+            let thread = thread.read(cx);
+            let markdown = thread.to_markdown(cx)?;
+            let thread_summary = thread
+                .summary()
+                .map(|summary| summary.to_string())
+                .unwrap_or_else(|| "Thread".to_string());
+
+            let project = workspace.project().clone();
+            let buffer = project.update(cx, |project, cx| {
+                project.create_local_buffer(&markdown, Some(markdown_language), cx)
+            });
+            let buffer =
+                cx.new(|cx| MultiBuffer::singleton(buffer, cx).with_title(thread_summary.clone()));
+
+            workspace.add_item_to_active_pane(
+                Box::new(cx.new(|cx| {
+                    let mut editor =
+                        Editor::for_multibuffer(buffer, Some(project.clone()), window, cx);
+                    editor.set_breadcrumb_header(thread_summary);
+                    editor
+                })),
+                None,
+                true,
+                window,
+                cx,
+            );
+
+            anyhow::Ok(())
+        })??;
+        anyhow::Ok(())
+    })
+}
+
 pub(crate) fn open_context(
     context: &AgentContextHandle,
     workspace: Entity<Workspace>,

crates/agent/src/assistant_panel.rs 🔗

@@ -49,7 +49,7 @@ use zed_actions::assistant::{OpenRulesLibrary, ToggleFocus};
 use zed_actions::{DecreaseBufferFontSize, IncreaseBufferFontSize, ResetBufferFontSize};
 use zed_llm_client::UsageLimit;
 
-use crate::active_thread::{ActiveThread, ActiveThreadEvent};
+use crate::active_thread::{self, ActiveThread, ActiveThreadEvent};
 use crate::agent_diff::AgentDiff;
 use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};
 use crate::history_store::{HistoryEntry, HistoryStore, RecentEntry};
@@ -1155,50 +1155,12 @@ impl AssistantPanel {
             return;
         };
 
-        let markdown_language_task = workspace
-            .read(cx)
-            .app_state()
-            .languages
-            .language_for_name("Markdown");
         let Some(thread) = self.active_thread() else {
             return;
         };
-        cx.spawn_in(window, async move |_this, cx| {
-            let markdown_language = markdown_language_task.await?;
-
-            workspace.update_in(cx, |workspace, window, cx| {
-                let thread = thread.read(cx);
-                let markdown = thread.to_markdown(cx)?;
-                let thread_summary = thread
-                    .summary()
-                    .map(|summary| summary.to_string())
-                    .unwrap_or_else(|| "Thread".to_string());
-
-                let project = workspace.project().clone();
-                let buffer = project.update(cx, |project, cx| {
-                    project.create_local_buffer(&markdown, Some(markdown_language), cx)
-                });
-                let buffer = cx.new(|cx| {
-                    MultiBuffer::singleton(buffer, cx).with_title(thread_summary.clone())
-                });
-
-                workspace.add_item_to_active_pane(
-                    Box::new(cx.new(|cx| {
-                        let mut editor =
-                            Editor::for_multibuffer(buffer, Some(project.clone()), window, cx);
-                        editor.set_breadcrumb_header(thread_summary);
-                        editor
-                    })),
-                    None,
-                    true,
-                    window,
-                    cx,
-                );
 
-                anyhow::Ok(())
-            })
-        })
-        .detach_and_log_err(cx);
+        active_thread::open_active_thread_as_markdown(thread, workspace, window, cx)
+            .detach_and_log_err(cx);
     }
 
     fn handle_assistant_configuration_event(