diff --git a/codex-rs/core/src/context_manager/history_tests.rs b/codex-rs/core/src/context_manager/history_tests.rs index b678c2e6c..7ef6a3410 100644 --- a/codex-rs/core/src/context_manager/history_tests.rs +++ b/codex-rs/core/src/context_manager/history_tests.rs @@ -421,9 +421,23 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_supported() { ResponseItem::Message { id: None, role: "user".to_string(), - content: vec![ContentItem::InputImage { - image_url: "data:image/png;base64,Zm9v".to_string(), - }], + content: vec![ + ContentItem::InputText { + text: "Image Generation Call".to_string(), + }, + ContentItem::InputText { + text: "Image ID: ig_123".to_string(), + }, + ContentItem::InputText { + text: "Prompt: lobster".to_string(), + }, + ContentItem::InputImage { + image_url: "data:image/png;base64,Zm9v".to_string(), + }, + ContentItem::InputText { + text: "Saved to: CWD".to_string(), + }, + ], end_turn: None, phase: None, }, @@ -475,10 +489,24 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_unsupported() { ResponseItem::Message { id: None, role: "user".to_string(), - content: vec![ContentItem::InputText { - text: "image content omitted because you do not support image input" - .to_string(), - }], + content: vec![ + ContentItem::InputText { + text: "Image Generation Call".to_string(), + }, + ContentItem::InputText { + text: "Image ID: ig_123".to_string(), + }, + ContentItem::InputText { + text: "Prompt: lobster".to_string(), + }, + ContentItem::InputText { + text: "image content omitted because you do not support image input" + .to_string(), + }, + ContentItem::InputText { + text: "Saved to: CWD".to_string(), + }, + ], end_turn: None, phase: None, }, diff --git a/codex-rs/core/src/context_manager/normalize.rs b/codex-rs/core/src/context_manager/normalize.rs index 465edae41..95d36f2f5 100644 --- a/codex-rs/core/src/context_manager/normalize.rs +++ b/codex-rs/core/src/context_manager/normalize.rs @@ -215,17 +215,37 @@ pub(crate) fn rewrite_image_generation_calls_for_stateless_input(items: &mut Vec *items = original_items .into_iter() .map(|item| match item { - ResponseItem::ImageGenerationCall { result, .. } => { + ResponseItem::ImageGenerationCall { + id, + revised_prompt, + result, + .. + } => { let image_url = if result.starts_with("data:") { result } else { format!("data:image/png;base64,{result}") }; + let revised_prompt = revised_prompt.unwrap_or_default(); ResponseItem::Message { id: None, role: "user".to_string(), - content: vec![ContentItem::InputImage { image_url }], + content: vec![ + ContentItem::InputText { + text: "Image Generation Call".to_string(), + }, + ContentItem::InputText { + text: format!("Image ID: {id}"), + }, + ContentItem::InputText { + text: format!("Prompt: {revised_prompt}"), + }, + ContentItem::InputImage { image_url }, + ContentItem::InputText { + text: "Saved to: CWD".to_string(), + }, + ], end_turn: None, phase: None, } diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 35a5a12d3..aff0a9f41 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2353,10 +2353,15 @@ impl ChatWidget { fn on_image_generation_end(&mut self, event: ImageGenerationEndEvent) { self.flush_answer_stream_with_separator(); + let saved_to = event.saved_path.as_deref().and_then(|saved_path| { + std::path::Path::new(saved_path) + .parent() + .map(|parent| parent.display().to_string()) + }); self.add_to_history(history_cell::new_image_generation_call( event.call_id, - event.status, event.revised_prompt, + saved_to, )); self.request_redraw(); } diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_generation_call_history_snapshot.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_generation_call_history_snapshot.snap index 9ed57a368..05f2b371c 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_generation_call_history_snapshot.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_generation_call_history_snapshot.snap @@ -3,5 +3,6 @@ source: tui/src/chatwidget/tests.rs assertion_line: 5889 expression: combined --- -• Generated Image - └ completed: A tiny blue square +• Generated Image: + └ A tiny blue square + └ Saved to: /tmp/project diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index b20bde081..98e5005c7 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -6288,7 +6288,7 @@ async fn image_generation_call_adds_history_cell() { status: "completed".into(), revised_prompt: Some("A tiny blue square".into()), result: "Zm9v".into(), - saved_path: None, + saved_path: Some("/tmp/project/ig-1.png".into()), }), }); diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 1167a391e..e6feef2cf 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -2215,15 +2215,18 @@ pub(crate) fn new_view_image_tool_call(path: PathBuf, cwd: &Path) -> PlainHistor pub(crate) fn new_image_generation_call( call_id: String, - status: String, revised_prompt: Option, + saved_to: Option, ) -> PlainHistoryCell { let detail = revised_prompt.unwrap_or_else(|| call_id.clone()); - let lines: Vec> = vec![ - vec!["• ".dim(), "Generated Image".bold()].into(), - vec![" └ ".dim(), format!("{status}: {detail}").dim()].into(), + let mut lines: Vec> = vec![ + vec!["• ".dim(), "Generated Image:".bold()].into(), + vec![" └ ".dim(), detail.dim()].into(), ]; + if let Some(saved_to) = saved_to { + lines.push(vec![" └ ".dim(), format!("Saved to: {saved_to}").dim()].into()); + } PlainHistoryCell { lines } }