diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__binary_size_ideal_response.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__binary_size_ideal_response.snap index e3121774..77738439 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__binary_size_ideal_response.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__binary_size_ideal_response.snap @@ -1,6 +1,5 @@ --- source: tui/src/chatwidget/tests.rs -assertion_line: 1152 expression: "lines[start_idx..].join(\"\\n\")" --- • I need to check the codex-rs repository to explain why the project's binaries @@ -33,7 +32,7 @@ expression: "lines[start_idx..].join(\"\\n\")" │ … +1 lines └ --- ansi-escape/Cargo.toml [package] - … +7 lines + … +243 lines ] } tracing = { version diff --git a/codex-rs/tui/src/exec_cell/render.rs b/codex-rs/tui/src/exec_cell/render.rs index a3cc8cac..98d9d072 100644 --- a/codex-rs/tui/src/exec_cell/render.rs +++ b/codex-rs/tui/src/exec_cell/render.rs @@ -48,10 +48,16 @@ pub(crate) fn new_active_exec_command( }) } +#[derive(Clone)] +pub(crate) struct OutputLines { + pub(crate) lines: Vec>, + pub(crate) omitted: Option, +} + pub(crate) fn output_lines( output: Option<&CommandOutput>, params: OutputLinesParams, -) -> Vec> { +) -> OutputLines { let OutputLinesParams { only_err, include_angle_pipe, @@ -63,9 +69,19 @@ pub(crate) fn output_lines( stderr, .. } = match output { - Some(output) if only_err && output.exit_code == 0 => return vec![], + Some(output) if only_err && output.exit_code == 0 => { + return OutputLines { + lines: Vec::new(), + omitted: None, + }; + } Some(output) => output, - None => return vec![], + None => { + return OutputLines { + lines: Vec::new(), + omitted: None, + }; + } }; let src = if *exit_code == 0 { stdout } else { stderr }; @@ -73,7 +89,7 @@ pub(crate) fn output_lines( let total = lines.len(); let limit = TOOL_CALL_MAX_LINES; - let mut out = Vec::new(); + let mut out: Vec> = Vec::new(); let head_end = total.min(limit); for (i, raw) in lines[..head_end].iter().enumerate() { @@ -93,6 +109,11 @@ pub(crate) fn output_lines( } let show_ellipsis = total > 2 * limit; + let omitted = if show_ellipsis { + Some(total - 2 * limit) + } else { + None + }; if show_ellipsis { let omitted = total - 2 * limit; out.push(format!("… +{omitted} lines").into()); @@ -114,7 +135,10 @@ pub(crate) fn output_lines( out.push(line); } - out + OutputLines { + lines: out, + omitted, + } } pub(crate) fn spinner(start_time: Option) -> Span<'static> { @@ -371,7 +395,7 @@ impl ExecCell { } if let Some(output) = call.output.as_ref() { - let raw_output_lines = output_lines( + let raw_output = output_lines( Some(output), OutputLinesParams { only_err: false, @@ -380,15 +404,18 @@ impl ExecCell { }, ); - if raw_output_lines.is_empty() { + if raw_output.lines.is_empty() { lines.extend(prefix_lines( vec![Line::from("(no output)".dim())], Span::from(layout.output_block.initial_prefix).dim(), Span::from(layout.output_block.subsequent_prefix), )); } else { - let trimmed_output = - Self::truncate_lines_middle(&raw_output_lines, layout.output_max_lines); + let trimmed_output = Self::truncate_lines_middle( + &raw_output.lines, + layout.output_max_lines, + raw_output.omitted, + ); let mut wrapped_output: Vec> = Vec::new(); let output_wrap_width = layout.output_block.wrap_width(width); @@ -427,7 +454,11 @@ impl ExecCell { out } - fn truncate_lines_middle(lines: &[Line<'static>], max: usize) -> Vec> { + fn truncate_lines_middle( + lines: &[Line<'static>], + max: usize, + omitted_hint: Option, + ) -> Vec> { if max == 0 { return Vec::new(); } @@ -435,7 +466,17 @@ impl ExecCell { return lines.to_vec(); } if max == 1 { - return vec![Self::ellipsis_line(lines.len())]; + // Carry forward any previously omitted count and add any + // additionally hidden content lines from this truncation. + let base = omitted_hint.unwrap_or(0); + // When an existing ellipsis is present, `lines` already includes + // that single representation line; exclude it from the count of + // additionally omitted content lines. + let extra = lines + .len() + .saturating_sub(usize::from(omitted_hint.is_some())); + let omitted = base + extra; + return vec![Self::ellipsis_line(omitted)]; } let head = (max - 1) / 2; @@ -446,8 +487,12 @@ impl ExecCell { out.extend(lines[..head].iter().cloned()); } - let omitted = lines.len().saturating_sub(head + tail); - out.push(Self::ellipsis_line(omitted)); + let base = omitted_hint.unwrap_or(0); + let additional = lines + .len() + .saturating_sub(head + tail) + .saturating_sub(usize::from(omitted_hint.is_some())); + out.push(Self::ellipsis_line(base + additional)); if tail > 0 { out.extend(lines[lines.len() - tail..].iter().cloned()); diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index aa0ae35d..2cc0c3fd 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -1148,7 +1148,7 @@ pub(crate) fn new_patch_apply_failure(stderr: String) -> PlainHistoryCell { lines.push(Line::from("✘ Failed to apply patch".magenta().bold())); if !stderr.trim().is_empty() { - lines.extend(output_lines( + let output = output_lines( Some(&CommandOutput { exit_code: 1, stdout: String::new(), @@ -1160,7 +1160,8 @@ pub(crate) fn new_patch_apply_failure(stderr: String) -> PlainHistoryCell { include_angle_pipe: true, include_prefix: true, }, - )); + ); + lines.extend(output.lines); } PlainHistoryCell { lines }