mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Add Markdown conversation export to the TUI (#37358)
## What changed - Add `/export` with clipboard and file destinations, including a default filename prompt and support for an explicit path argument. - Export the complete conversation history as structured Markdown, preserving user and assistant messages, plans, reasoning, activity, image labels, file changes, and MCP tool details while honoring reasoning visibility. - Fall back to legacy history loading when pagination is unavailable, and use the visible transcript for ephemeral sessions. - Resolve relative and home-relative paths without overwriting an existing file, and report export or copy results in the conversation. ## Testing - Cover Markdown rendering, hidden review prompts, activity details, complete paginated and legacy history loading, destination prompts, status messages, path resolution, and overwrite protection. GitOrigin-RevId: c2815fc2430d7a555da75d7038cc1ac08157b82e
This commit is contained in:
1 parent
4d7e3e90d9
commit
2801d12661
19 files changed
+795
-3
No files matched your search
@@ -0,0 +1,74 @@
|
||||
//! Destination and filename prompts for on-demand transcript exports.
|
||||
|
||||
use super::*;
|
||||
use crate::app_event::TranscriptExportDestination;
|
||||
|
||||
impl ChatWidget {
|
||||
pub(crate) fn copy_transcript_to_clipboard(&mut self, markdown: &str) {
|
||||
match crate::clipboard_copy::copy_to_clipboard(markdown) {
|
||||
Ok(lease) => {
|
||||
self.clipboard_lease = lease;
|
||||
self.add_info_message(
|
||||
"Copied conversation to clipboard".to_string(),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
Err(error) => self.add_error_message(format!("Copy failed: {error}")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn show_transcript_export_popup(&mut self) {
|
||||
self.show_selection_view(SelectionViewParams {
|
||||
title: Some("Export conversation".to_string()),
|
||||
subtitle: Some("Save the complete conversation as Markdown".to_string()),
|
||||
footer_hint: Some(standard_popup_hint_line()),
|
||||
items: vec![
|
||||
SelectionItem {
|
||||
name: "Copy to clipboard".to_string(),
|
||||
description: Some("Copy the complete Markdown transcript".to_string()),
|
||||
is_disabled: cfg!(target_os = "android"),
|
||||
actions: vec![Box::new(|tx| {
|
||||
tx.send(AppEvent::ExportTranscript {
|
||||
destination: TranscriptExportDestination::Clipboard,
|
||||
});
|
||||
})],
|
||||
dismiss_on_select: true,
|
||||
..Default::default()
|
||||
},
|
||||
SelectionItem {
|
||||
name: "Save to file".to_string(),
|
||||
description: Some("Choose a Markdown filename".to_string()),
|
||||
actions: vec![Box::new(|tx| {
|
||||
tx.send(AppEvent::OpenTranscriptExportFilePrompt);
|
||||
})],
|
||||
dismiss_on_select: true,
|
||||
..Default::default()
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
});
|
||||
self.defer_input_until_settings_applied();
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub(crate) fn show_transcript_export_file_prompt(&mut self) {
|
||||
let tx = self.app_event_tx.clone();
|
||||
let filename = self.thread_id().map_or_else(
|
||||
|| "codex-session.md".to_string(),
|
||||
|thread_id| format!("codex-session-{thread_id}.md"),
|
||||
);
|
||||
let view = CustomPromptView::new(
|
||||
"Save conversation".to_string(),
|
||||
"Type a filename and press Enter".to_string(),
|
||||
filename,
|
||||
/*context_label*/ None,
|
||||
Box::new(move |filename| {
|
||||
tx.send(AppEvent::ExportTranscript {
|
||||
destination: TranscriptExportDestination::File(PathBuf::from(filename)),
|
||||
});
|
||||
}),
|
||||
);
|
||||
self.bottom_pane.show_view(Box::new(view));
|
||||
self.request_redraw();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user