diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 3a3f5d407be..e61b32589be 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -1060,6 +1060,7 @@ impl ChatComposer { &expanded.text_elements, ); self.pending_pastes.clear(); + self.large_paste_counters.clear(); self.textarea.set_text_clearing_elements(""); return ( InputResult::Submitted { @@ -1839,6 +1840,7 @@ impl ChatComposer { }); } self.pending_pastes.clear(); + self.large_paste_counters.clear(); Some((text, text_elements)) } @@ -4704,6 +4706,37 @@ mod tests { assert_eq!(composer.pending_pastes[1].0, third); } + #[test] + fn large_paste_numbering_resets_after_submission() { + use crossterm::event::KeyCode; + use crossterm::event::KeyEvent; + use crossterm::event::KeyModifiers; + + let (tx, _rx) = unbounded_channel::(); + let sender = AppEventSender::new(tx); + let mut composer = ChatComposer::new( + true, + sender, + false, + "Ask Codex to do anything".to_string(), + false, + ); + + let paste = "x".repeat(LARGE_PASTE_CHAR_THRESHOLD + 4); + let base = format!("[Pasted Content {} chars]", paste.chars().count()); + let second = format!("{base} #2"); + + composer.handle_paste(paste.clone()); + composer.handle_paste(paste.clone()); + assert_eq!(composer.textarea.text(), format!("{base}{second}")); + + let _ = composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + assert_eq!(composer.textarea.text(), ""); + + composer.handle_paste(paste); + assert_eq!(composer.textarea.text(), base); + } + #[test] fn test_partial_placeholder_deletion() { use crossterm::event::KeyCode; @@ -6079,6 +6112,50 @@ mod tests { assert!(composer.pending_pastes.is_empty()); } + #[test] + fn numeric_prompt_auto_submit_resets_large_paste_numbering() { + let (tx, _rx) = unbounded_channel::(); + let sender = AppEventSender::new(tx); + let mut composer = ChatComposer::new( + true, + sender, + false, + "Ask Codex to do anything".to_string(), + false, + ); + composer.set_steer_enabled(true); + + composer.set_custom_prompts(vec![CustomPrompt { + name: "my-prompt".to_string(), + path: "/tmp/my-prompt.md".to_string().into(), + content: "Echo: $1".to_string(), + description: None, + argument_hint: None, + }]); + + let paste = "x".repeat(LARGE_PASTE_CHAR_THRESHOLD + 5); + let base = format!("[Pasted Content {} chars]", paste.chars().count()); + let second = format!("{base} #2"); + + composer.handle_paste(paste.clone()); + composer.handle_paste(paste.clone()); + assert_eq!(composer.textarea.text(), format!("{base}{second}")); + + composer + .textarea + .set_text_clearing_elements("/prompts:my-prompt "); + composer.textarea.set_cursor(composer.textarea.text().len()); + composer.handle_paste(paste.clone()); + + let (result, _needs_redraw) = + composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + assert!(matches!(result, InputResult::Submitted { .. })); + assert_eq!(composer.textarea.text(), ""); + + composer.handle_paste(paste); + assert_eq!(composer.textarea.text(), base); + } + #[test] fn queued_prompt_submission_prunes_unused_image_attachments() { let (tx, _rx) = unbounded_channel::();