From 5552688621340c7042a9240a9bedc864242c5ee7 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Thu, 14 Aug 2025 19:49:42 -0700 Subject: [PATCH] Format multiline commands (#2333) image image --- codex-rs/tui/src/chatwidget.rs | 12 --- codex-rs/tui/src/history_cell.rs | 8 -- codex-rs/tui/src/user_approval_widget.rs | 118 ++++++++++++++--------- 3 files changed, 75 insertions(+), 63 deletions(-) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index c10b94b7..c39edb82 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -43,7 +43,6 @@ use crate::bottom_pane::BottomPane; use crate::bottom_pane::BottomPaneParams; use crate::bottom_pane::CancellationEvent; use crate::bottom_pane::InputResult; -use crate::exec_command::strip_bash_lc_and_escape; use crate::history_cell; use crate::history_cell::CommandOutput; use crate::history_cell::ExecCell; @@ -391,17 +390,6 @@ impl ChatWidget<'_> { pub(crate) fn handle_exec_approval_now(&mut self, id: String, ev: ExecApprovalRequestEvent) { self.flush_answer_stream_with_separator(); - // Log a background summary immediately so the history is chronological. - let cmdline = strip_bash_lc_and_escape(&ev.command); - let text = format!( - "command requires approval:\n$ {cmdline}{reason}", - reason = ev - .reason - .as_ref() - .map(|r| format!("\n{r}")) - .unwrap_or_default() - ); - self.add_to_history(&history_cell::new_background_event(text)); let request = ApprovalRequest::Exec { id, diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index a4cebe98..2a17ff16 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -136,14 +136,6 @@ fn pretty_provider_name(id: &str) -> String { } } -pub(crate) fn new_background_event(message: String) -> PlainHistoryCell { - let mut lines: Vec> = Vec::new(); - lines.push(Line::from("event".dim())); - lines.extend(message.lines().map(|line| ansi_escape_line(line).dim())); - lines.push(Line::from("")); - PlainHistoryCell { lines } -} - pub(crate) fn new_session_info( config: &Config, event: SessionConfiguredEvent, diff --git a/codex-rs/tui/src/user_approval_widget.rs b/codex-rs/tui/src/user_approval_widget.rs index 78ece561..7a4e3d90 100644 --- a/codex-rs/tui/src/user_approval_widget.rs +++ b/codex-rs/tui/src/user_approval_widget.rs @@ -109,6 +109,34 @@ pub(crate) struct UserApprovalWidget<'a> { done: bool, } +fn to_command_display<'a>( + first_line: Vec>, + cmd: String, + last_line: Vec>, +) -> Vec> { + let command_lines: Vec = cmd + .lines() + .map(|line| Span::from(line.to_string()).style(Style::new().add_modifier(Modifier::DIM))) + .collect(); + + let mut lines: Vec> = vec![]; + + let mut first_line = first_line.clone(); + if command_lines.len() == 1 { + first_line.push(command_lines[0].clone()); + first_line.extend(last_line); + } else { + for line in command_lines { + lines.push(Line::from(vec![Span::from(" "), line])); + } + let last_line = last_line.clone(); + lines.push(Line::from(last_line)); + } + lines.insert(0, Line::from(first_line)); + + lines +} + impl UserApprovalWidget<'_> { pub(crate) fn new(approval_request: ApprovalRequest, app_event_tx: AppEventSender) -> Self { let confirmation_prompt = match &approval_request { @@ -116,17 +144,13 @@ impl UserApprovalWidget<'_> { command, reason, .. } => { let cmd = strip_bash_lc_and_escape(command); - // Present a single-line summary without cwd: "codex wants to run: " - let mut cmd_span: Span = cmd.clone().into(); - cmd_span.style = cmd_span.style.add_modifier(Modifier::DIM); - let mut contents: Vec = vec![ - Line::from(vec![ - "? ".fg(Color::Blue), - "Codex wants to run ".bold(), - cmd_span, - ]), - Line::from(""), - ]; + let mut contents: Vec = to_command_display( + vec!["? ".fg(Color::Blue), "Codex wants to run ".bold()], + cmd, + vec![], + ); + + contents.push(Line::from("")); if let Some(reason) = reason { contents.push(Line::from(reason.clone().italic())); contents.push(Line::from("")); @@ -244,44 +268,52 @@ impl UserApprovalWidget<'_> { // Result line based on decision. match decision { ReviewDecision::Approved => { - lines.push(Line::from(vec![ - "✔ ".fg(Color::Green), - "You ".into(), - "approved".bold(), - " codex to run ".into(), - cmd_span, - " ".into(), - "this time".bold(), - ])); + lines.extend(to_command_display( + vec![ + "✔ ".fg(Color::Green), + "You ".into(), + "approved".bold(), + " codex to run ".into(), + ], + cmd, + vec![" this time".bold()], + )); } ReviewDecision::ApprovedForSession => { - lines.push(Line::from(vec![ - "✔ ".fg(Color::Green), - "You ".into(), - "approved".bold(), - " codex to run ".into(), - cmd_span, - " ".into(), - "every time this session".bold(), - ])); + lines.extend(to_command_display( + vec![ + "✔ ".fg(Color::Green), + "You ".into(), + "approved".bold(), + "codex to run ".into(), + ], + cmd, + vec![" every time this session".bold()], + )); } ReviewDecision::Denied => { - lines.push(Line::from(vec![ - "✗ ".fg(Color::Red), - "You ".into(), - "did not approve".bold(), - " codex to run ".into(), - cmd_span, - ])); + lines.extend(to_command_display( + vec![ + "✗ ".fg(Color::Red), + "You ".into(), + "did not approve".bold(), + " codex to run ".into(), + ], + cmd, + vec![], + )); } ReviewDecision::Abort => { - lines.push(Line::from(vec![ - "✗ ".fg(Color::Red), - "You ".into(), - "canceled".bold(), - " the request to run ".into(), - cmd_span, - ])); + lines.extend(to_command_display( + vec![ + "✗ ".fg(Color::Red), + "You ".into(), + "canceled".bold(), + " the request to run ".into(), + ], + cmd, + vec![], + )); } } }