diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 8b10d224dd..28c11becc5 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -144,7 +144,9 @@ impl ChatComposer { pub fn desired_height(&self, width: u16) -> u16 { self.textarea.desired_height(width - 1) + match &self.active_popup { - ActivePopup::None => 1u16, + ActivePopup::None => { + if self.token_usage_info.is_some() && width < 90 { 2 } else { 1 } + } ActivePopup::Command(c) => c.calculate_required_height(), ActivePopup::File(c) => c.calculate_required_height(), } @@ -154,7 +156,7 @@ impl ChatComposer { let popup_height = match &self.active_popup { ActivePopup::Command(popup) => popup.calculate_required_height(), ActivePopup::File(popup) => popup.calculate_required_height(), - ActivePopup::None => 1, + ActivePopup::None => if self.token_usage_info.is_some() && area.width < 90 { 2 } else { 1 }, }; let [textarea_rect, _] = Layout::vertical([Constraint::Min(1), Constraint::Max(popup_height)]).areas(area); @@ -1143,10 +1145,18 @@ impl ChatComposer { impl WidgetRef for ChatComposer { fn render_ref(&self, area: Rect, buf: &mut Buffer) { + // Determine if we should render a second footer row for tokens/context. + let needs_tokens_line = self.token_usage_info.is_some() && area.width < 90; let popup_height = match &self.active_popup { ActivePopup::Command(popup) => popup.calculate_required_height(), ActivePopup::File(popup) => popup.calculate_required_height(), - ActivePopup::None => 1, + ActivePopup::None => { + if needs_tokens_line { + 2 + } else { + 1 + } + } }; let [textarea_rect, popup_rect] = Layout::vertical([Constraint::Min(1), Constraint::Max(popup_height)]).areas(area); @@ -1191,35 +1201,68 @@ impl WidgetRef for ChatComposer { hint.push(Span::from(" edit prev")); } - // Append token/context usage info to the footer hints when available. - if let Some(token_usage_info) = &self.token_usage_info { - let token_usage = &token_usage_info.total_token_usage; - hint.push(Span::from(" ")); - hint.push( - Span::from(format!("{} tokens used", token_usage.blended_total())) - .style(Style::default().add_modifier(Modifier::DIM)), - ); - let last_token_usage = &token_usage_info.last_token_usage; - if let Some(context_window) = token_usage_info.model_context_window { - let percent_remaining: u8 = if context_window > 0 { - last_token_usage.percent_of_context_window_remaining( - context_window, - token_usage_info.initial_prompt_tokens, - ) - } else { - 100 - }; + if !needs_tokens_line { + if let Some(token_usage_info) = &self.token_usage_info { + let token_usage = &token_usage_info.total_token_usage; hint.push(Span::from(" ")); hint.push( - Span::from(format!("{percent_remaining}% context left")) + Span::from(format!("{} tokens used", token_usage.blended_total())) .style(Style::default().add_modifier(Modifier::DIM)), ); + let last_token_usage = &token_usage_info.last_token_usage; + if let Some(context_window) = token_usage_info.model_context_window { + let percent_remaining: u8 = if context_window > 0 { + last_token_usage.percent_of_context_window_remaining( + context_window, + token_usage_info.initial_prompt_tokens, + ) + } else { + 100 + }; + hint.push(Span::from(" ")); + hint.push( + Span::from(format!("{percent_remaining}% context left")) + .style(Style::default().add_modifier(Modifier::DIM)), + ); + } } } Line::from(hint) .style(Style::default().dim()) .render_ref(bottom_line_rect, buf); + + if needs_tokens_line { + if let Some(token_usage_info) = &self.token_usage_info { + let token_usage = &token_usage_info.total_token_usage; + let mut text = String::from(" "); + text.push_str(&format!("{} tokens used", token_usage.blended_total())); + if let Some(context_window) = token_usage_info.model_context_window { + let last_token_usage = &token_usage_info.last_token_usage; + let percent_remaining: u8 = if context_window > 0 { + last_token_usage.percent_of_context_window_remaining( + context_window, + token_usage_info.initial_prompt_tokens, + ) + } else { + 100 + }; + text.push_str(" "); + text.push_str(&format!("{percent_remaining}% context left")); + } + Line::from(text) + .style(Style::default().dim()) + .render_ref( + Rect::new( + bottom_line_rect.x, + bottom_line_rect.y + 1, + bottom_line_rect.width, + 1, + ), + buf, + ); + } + } } } let border_style = if self.has_focus {