mirror of
https://github.com/openai/codex.git
synced 2026-10-25 18:28:38 +00:00
fix(tui): centralize usable-width guards for narrow layouts
This commit is contained in:
committed by
Felipe Coury
parent
8a94c65989
commit
be9006c161
@@ -4189,7 +4189,7 @@ impl ChatWidget {
|
||||
self.last_rendered_width
|
||||
.get()
|
||||
.or_else(|| terminal::size().ok().map(|(w, _)| usize::from(w)))
|
||||
.map(|w| w.saturating_sub(reserved_cols))
|
||||
.and_then(|w| crate::width::usable_content_width(w, reserved_cols))
|
||||
}
|
||||
|
||||
pub(crate) fn on_terminal_resize(&mut self, width: u16) {
|
||||
|
||||
@@ -111,6 +111,21 @@ async fn turn_started_uses_runtime_context_window_before_first_token_count() {
|
||||
"expected /status to avoid raw config context window, got: {context_line}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn current_stream_width_returns_none_when_reserved_columns_exhaust_width() {
|
||||
let (chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
chat.last_rendered_width.set(Some(2));
|
||||
assert_eq!(chat.current_stream_width(2), None);
|
||||
|
||||
chat.last_rendered_width.set(Some(4));
|
||||
assert_eq!(chat.current_stream_width(4), None);
|
||||
|
||||
chat.last_rendered_width.set(Some(5));
|
||||
assert_eq!(chat.current_stream_width(4), Some(1));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn helpers_are_available_and_do_not_panic() {
|
||||
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
|
||||
|
||||
@@ -407,7 +407,7 @@ impl ReasoningSummaryCell {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
append_markdown(
|
||||
&self.content,
|
||||
Some((width as usize).saturating_sub(2)),
|
||||
crate::width::usable_content_width_u16(width, 2),
|
||||
Some(self.cwd.as_path()),
|
||||
&mut lines,
|
||||
);
|
||||
@@ -509,10 +509,13 @@ impl AgentMarkdownCell {
|
||||
|
||||
impl HistoryCell for AgentMarkdownCell {
|
||||
fn display_lines(&self, width: u16) -> Vec<Line<'static>> {
|
||||
let Some(wrap_width) = crate::width::usable_content_width_u16(width, 2) else {
|
||||
return prefix_lines(vec![Line::default()], "• ".dim(), " ".into());
|
||||
};
|
||||
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
// Re-render markdown from source at the current width. Reserve 2 columns for the "• " /
|
||||
// " " prefix prepended below.
|
||||
let wrap_width = (width as usize).saturating_sub(2);
|
||||
crate::markdown::append_markdown_agent_with_cwd(
|
||||
&self.markdown_source,
|
||||
Some(wrap_width),
|
||||
@@ -4807,6 +4810,51 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_markdown_cell_narrow_width_shows_prefix_only() {
|
||||
let source = "| Name | Role |\n|------|------|\n| Alice | Engineer |\n";
|
||||
let cell = AgentMarkdownCell::new(source.to_string());
|
||||
|
||||
let lines = render_lines(&cell.display_lines(2));
|
||||
assert_eq!(lines, vec!["• ".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_and_prefixed_cells_handle_tiny_widths() {
|
||||
let user_cell = UserHistoryCell {
|
||||
message: "tiny width coverage for wrapped user history".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
local_image_paths: Vec::new(),
|
||||
};
|
||||
let agent_message_cell = AgentMessageCell::new(vec!["tiny width agent line".into()], true);
|
||||
let reasoning_cell = ReasoningSummaryCell::new(
|
||||
"Plan".to_string(),
|
||||
"Reasoning summary content for tiny widths.".to_string(),
|
||||
false,
|
||||
);
|
||||
let agent_markdown_cell =
|
||||
AgentMarkdownCell::new("| A | B |\n|---|---|\n| x | y |\n".to_string());
|
||||
|
||||
for width in 1..=4 {
|
||||
assert!(
|
||||
!user_cell.display_lines(width).is_empty(),
|
||||
"user cell should render at width {width}",
|
||||
);
|
||||
assert!(
|
||||
!agent_message_cell.display_lines(width).is_empty(),
|
||||
"agent message cell should render at width {width}",
|
||||
);
|
||||
assert!(
|
||||
!reasoning_cell.display_lines(width).is_empty(),
|
||||
"reasoning cell should render at width {width}",
|
||||
);
|
||||
assert!(
|
||||
!agent_markdown_cell.display_lines(width).is_empty(),
|
||||
"agent markdown cell should render at width {width}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_markdown_cell_survives_insert_history_rewrap() {
|
||||
let source = "\
|
||||
|
||||
@@ -208,6 +208,7 @@ mod voice {
|
||||
pub(crate) fn clear(&self) {}
|
||||
}
|
||||
}
|
||||
mod width;
|
||||
|
||||
mod wrapping;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/// Returns usable content width after reserving fixed columns.
|
||||
///
|
||||
/// Guarantees a strict positive width (`Some(n)` where `n > 0`) or `None` when
|
||||
/// the reserved columns consume the full width.
|
||||
pub(crate) fn usable_content_width(total_width: usize, reserved_cols: usize) -> Option<usize> {
|
||||
total_width
|
||||
.checked_sub(reserved_cols)
|
||||
.filter(|remaining| *remaining > 0)
|
||||
}
|
||||
|
||||
/// `u16` convenience wrapper around [`usable_content_width`].
|
||||
pub(crate) fn usable_content_width_u16(total_width: u16, reserved_cols: u16) -> Option<usize> {
|
||||
usable_content_width(usize::from(total_width), usize::from(reserved_cols))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn usable_content_width_returns_none_when_reserved_exhausts_width() {
|
||||
assert_eq!(usable_content_width(0, 0), None);
|
||||
assert_eq!(usable_content_width(2, 2), None);
|
||||
assert_eq!(usable_content_width(3, 4), None);
|
||||
assert_eq!(usable_content_width(5, 4), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usable_content_width_u16_matches_usize_variant() {
|
||||
assert_eq!(usable_content_width_u16(2, 2), None);
|
||||
assert_eq!(usable_content_width_u16(5, 4), Some(1));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user