From 4b0f5eb6a825578b43a8605ed07e9981009e70aa Mon Sep 17 00:00:00 2001 From: Jeremy Rose <172423086+nornagon-openai@users.noreply.github.com> Date: Tue, 7 Oct 2025 11:32:13 -0700 Subject: [PATCH] tui: wrapping bugfix (#4674) this fixes an issue where text lines with long words would sometimes overflow. - the default penalties for the OptimalFit algorithm allow overflowing in some cases. this seems insane to me, and i considered just banning the OptimalFit algorithm by disabling the 'smawk' feature on textwrap, but decided to keep it and just bump the overflow penalty to ~infinity since optimal fit does sometimes produce nicer wrappings. it's not clear this is worth it, though, and maybe we should just dump the optimal fit algorithm completely. - user history messages weren't rendering with the same wrap algorithm as used in the composer, which sometimes resulted in wrapping messages differently in the history vs. in the composer. --- codex-rs/tui/src/history_cell.rs | 3 ++- codex-rs/tui/src/wrapping.rs | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index e3a89da3..61640c33 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -104,7 +104,8 @@ impl HistoryCell for UserHistoryCell { .lines() .map(|l| Line::from(l).style(style)) .collect::>(), - RtOptions::new(wrap_width as usize), + // Wrap algorithm matches textarea.rs. + RtOptions::new(wrap_width as usize).wrap_algorithm(textwrap::WrapAlgorithm::FirstFit), ); lines.push(Line::from("").style(style)); diff --git a/codex-rs/tui/src/wrapping.rs b/codex-rs/tui/src/wrapping.rs index da79f036..70ca2e46 100644 --- a/codex-rs/tui/src/wrapping.rs +++ b/codex-rs/tui/src/wrapping.rs @@ -2,6 +2,7 @@ use ratatui::text::Line; use ratatui::text::Span; use std::ops::Range; use textwrap::Options; +use textwrap::wrap_algorithms::Penalties; use crate::render::line_utils::push_owned_lines; @@ -90,7 +91,11 @@ impl<'a> RtOptions<'a> { subsequent_indent: Line::default(), break_words: true, word_separator: textwrap::WordSeparator::new(), - wrap_algorithm: textwrap::WrapAlgorithm::new(), + wrap_algorithm: textwrap::WrapAlgorithm::OptimalFit(Penalties { + // ~infinite overflow penalty, we never want to overflow a line. + overflow_penalty: usize::MAX / 4, + ..Default::default() + }), word_splitter: textwrap::WordSplitter::HyphenSplitter, } }