@@ -11,7 +11,7 @@ use assistant_slash_command::SlashCommandWorkingSet;
use assistant_tool::ToolWorkingSet;
use client::zed_urls;
-use editor::Editor;
+use editor::{Editor, MultiBuffer};
use fs::Fs;
use gpui::{
prelude::*, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter,
@@ -38,7 +38,10 @@ use crate::message_editor::MessageEditor;
use crate::thread::{Thread, ThreadError, ThreadId};
use crate::thread_history::{PastContext, PastThread, ThreadHistory};
use crate::thread_store::ThreadStore;
-use crate::{InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory};
+use crate::{
+ InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown, OpenConfiguration,
+ OpenHistory,
+};
pub fn init(cx: &mut App) {
cx.observe_new(
@@ -411,6 +414,70 @@ impl AssistantPanel {
}
}
+ pub(crate) fn open_active_thread_as_markdown(
+ &mut self,
+ _: &OpenActiveThreadAsMarkdown,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let Some(workspace) = self
+ .workspace
+ .upgrade()
+ .ok_or_else(|| anyhow!("workspace dropped"))
+ .log_err()
+ else {
+ return;
+ };
+
+ let markdown_language_task = workspace
+ .read(cx)
+ .app_state()
+ .languages
+ .language_for_name("Markdown");
+ let thread = self.active_thread(cx);
+ cx.spawn_in(window, |_this, mut cx| async move {
+ let markdown_language = markdown_language_task.await?;
+
+ workspace.update_in(&mut cx, |workspace, window, cx| {
+ let thread = thread.read(cx);
+ let markdown = thread.to_markdown()?;
+ 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()),
+ true,
+ window,
+ cx,
+ );
+ editor.set_breadcrumb_header(thread_summary);
+ editor
+ })),
+ None,
+ true,
+ window,
+ cx,
+ );
+
+ anyhow::Ok(())
+ })
+ })
+ .detach_and_log_err(cx);
+ }
+
fn handle_assistant_configuration_event(
&mut self,
_entity: &Entity<AssistantConfiguration>,
@@ -1011,6 +1078,7 @@ impl Render for AssistantPanel {
.on_action(cx.listener(|this, _: &OpenHistory, window, cx| {
this.open_history(window, cx);
}))
+ .on_action(cx.listener(Self::open_active_thread_as_markdown))
.on_action(cx.listener(Self::deploy_prompt_library))
.child(self.render_toolbar(cx))
.map(|parent| match self.active_view {
@@ -1,3 +1,4 @@
+use std::io::Write;
use std::sync::Arc;
use anyhow::{Context as _, Result};
@@ -794,6 +795,50 @@ impl Thread {
false
}
}
+
+ pub fn to_markdown(&self) -> Result<String> {
+ let mut markdown = Vec::new();
+
+ for message in self.messages() {
+ writeln!(
+ markdown,
+ "## {role}\n",
+ role = match message.role {
+ Role::User => "User",
+ Role::Assistant => "Assistant",
+ Role::System => "System",
+ }
+ )?;
+ writeln!(markdown, "{}\n", message.text)?;
+
+ for tool_use in self.tool_uses_for_message(message.id) {
+ writeln!(
+ markdown,
+ "**Use Tool: {} ({})**",
+ tool_use.name, tool_use.id
+ )?;
+ writeln!(markdown, "```json")?;
+ writeln!(
+ markdown,
+ "{}",
+ serde_json::to_string_pretty(&tool_use.input)?
+ )?;
+ writeln!(markdown, "```")?;
+ }
+
+ for tool_result in self.tool_results_for_message(message.id) {
+ write!(markdown, "**Tool Results: {}", tool_result.tool_use_id)?;
+ if tool_result.is_error {
+ write!(markdown, " (Error)")?;
+ }
+
+ writeln!(markdown, "**\n")?;
+ writeln!(markdown, "{}", tool_result.content)?;
+ }
+ }
+
+ Ok(String::from_utf8_lossy(&markdown).to_string())
+ }
}
#[derive(Debug, Clone)]