Files
codex/prs/bolinfest/PR-1897.md
2025-09-02 15:17:45 -07:00

6.8 KiB

PR #1897: Run command UI

Description

Edit how commands show:

image

Full Diff

diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs
index 128e1ae8c1..50fa776ec3 100644
--- a/codex-rs/tui/src/chatwidget.rs
+++ b/codex-rs/tui/src/chatwidget.rs
@@ -485,7 +485,7 @@ impl ChatWidget<'_> {
             EventMsg::ExecCommandEnd(ExecCommandEndEvent {
                 call_id,
                 exit_code,
-                duration,
+                duration: _,
                 stdout,
                 stderr,
             }) => {
@@ -498,7 +498,6 @@ impl ChatWidget<'_> {
                         exit_code,
                         stdout,
                         stderr,
-                        duration,
                     },
                 ));
             }
diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs
index c577ce17a0..5caedf98ab 100644
--- a/codex-rs/tui/src/history_cell.rs
+++ b/codex-rs/tui/src/history_cell.rs
@@ -38,7 +38,6 @@ pub(crate) struct CommandOutput {
     pub(crate) exit_code: i32,
     pub(crate) stdout: String,
     pub(crate) stderr: String,
-    pub(crate) duration: Duration,
 }
 
 pub(crate) enum PatchEventType {
@@ -122,7 +121,7 @@ pub(crate) enum HistoryCell {
     PatchApplyResult { view: TextBlock },
 }
 
-const TOOL_CALL_MAX_LINES: usize = 5;
+const TOOL_CALL_MAX_LINES: usize = 3;
 
 impl HistoryCell {
     /// Return a cloned, plain representation of the cell's lines suitable for
@@ -232,8 +231,11 @@ impl HistoryCell {
         let command_escaped = strip_bash_lc_and_escape(&command);
 
         let lines: Vec<Line<'static>> = vec![
-            Line::from(vec!["command".magenta(), " running...".dim()]),
-            Line::from(format!("$ {command_escaped}")),
+            Line::from(vec![
+                "▌ ".cyan(),
+                "Running command ".magenta(),
+                command_escaped.into(),
+            ]),
             Line::from(""),
         ];
 
@@ -247,34 +249,36 @@ impl HistoryCell {
             exit_code,
             stdout,
             stderr,
-            duration,
         } = output;
 
         let mut lines: Vec<Line<'static>> = Vec::new();
-
-        // Title depends on whether we have output yet.
-        let title_line = Line::from(vec![
-            "command".magenta(),
-            format!(
-                " (code: {}, duration: {})",
-                exit_code,
-                format_duration(duration)
-            )
-            .dim(),
-        ]);
-        lines.push(title_line);
+        let command_escaped = strip_bash_lc_and_escape(&command);
+        lines.push(Line::from(vec![
+            "⚡Ran command ".magenta(),
+            command_escaped.into(),
+        ]));
 
         let src = if exit_code == 0 { stdout } else { stderr };
 
-        let cmdline = strip_bash_lc_and_escape(&command);
-        lines.push(Line::from(format!("$ {cmdline}")));
         let mut lines_iter = src.lines();
-        for raw in lines_iter.by_ref().take(TOOL_CALL_MAX_LINES) {
-            lines.push(ansi_escape_line(raw).dim());
+        for (idx, raw) in lines_iter.by_ref().take(TOOL_CALL_MAX_LINES).enumerate() {
+            let mut line = ansi_escape_line(raw);
+            let prefix = if idx == 0 { "  ⎿ " } else { "    " };
+            line.spans.insert(0, prefix.into());
+            line.spans.iter_mut().for_each(|span| {
+                span.style = span.style.add_modifier(Modifier::DIM);
+            });
+            lines.push(line);
         }
         let remaining = lines_iter.count();
         if remaining > 0 {
-            lines.push(Line::from(format!("... {remaining} additional lines")).dim());
+            let mut more = Line::from(format!("... +{remaining} lines"));
+            // Continuation/ellipsis is treated as a subsequent line for prefixing
+            more.spans.insert(0, "    ".into());
+            more.spans.iter_mut().for_each(|span| {
+                span.style = span.style.add_modifier(Modifier::DIM);
+            });
+            lines.push(more);
         }
         lines.push(Line::from(""));

Review Comments

codex-rs/tui/src/history_cell.rs

@@ -36,6 +36,7 @@ pub(crate) struct CommandOutput {
     pub(crate) exit_code: i32,
     pub(crate) stdout: String,
     pub(crate) stderr: String,
+    #[allow(dead_code)]

Let's just delete this if we aren't going to use it.

@@ -120,7 +121,7 @@ pub(crate) enum HistoryCell {
     PatchApplyResult { view: TextBlock },
 }
 
-const TOOL_CALL_MAX_LINES: usize = 5;
+const TOOL_CALL_MAX_LINES: usize = 3;

In a separate PR, it might be nice to make this overridable via Config.

I thought this had been requested in the past, though it seems it was the number of lines available to the model rather than in the UI: https://github.com/openai/codex/pull/575

@@ -242,34 +246,39 @@ impl HistoryCell {
             exit_code,
             stdout,
             stderr,
-            duration,
+            duration: _,
         } = output;
 
         let mut lines: Vec<Line<'static>> = Vec::new();
-
-        // Title depends on whether we have output yet.
-        let title_line = Line::from(vec![
-            "command".magenta(),
-            format!(
-                " (code: {}, duration: {})",
-                exit_code,
-                format_duration(duration)
-            )
-            .dim(),
-        ]);
-        lines.push(title_line);
+        let command_escaped = strip_bash_lc_and_escape(&command);
+        lines.push(Line::from(vec![
+            "⚡Ran command ".magenta(),
+            command_escaped.into(),
+        ]));
 
         let src = if exit_code == 0 { stdout } else { stderr };
 
-        let cmdline = strip_bash_lc_and_escape(&command);
-        lines.push(Line::from(format!("$ {cmdline}")));
         let mut lines_iter = src.lines();
+        let mut is_first = true;
         for raw in lines_iter.by_ref().take(TOOL_CALL_MAX_LINES) {
-            lines.push(ansi_escape_line(raw).dim());
+            let mut line = ansi_escape_line(raw);

Note you can also use enumerate() to get the index in addition to the value and then you can check index == 0 instead of maintaining this mut is_first.