Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions codex-rs/tui/src/bottom_pane/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ impl BottomPane {
}
}

pub fn status_widget(&self) -> Option<&StatusIndicatorWidget> {
self.status.as_ref()
}

fn active_view(&self) -> Option<&dyn BottomPaneView> {
self.view_stack.last().map(std::convert::AsRef::as_ref)
}
Expand Down
15 changes: 15 additions & 0 deletions codex-rs/tui/src/chatwidget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ pub(crate) struct ChatWidget {
// List of ghost commits corresponding to each turn.
ghost_snapshots: Vec<GhostCommit>,
ghost_snapshots_disabled: bool,
// Whether to add a final message separator after the last message
needs_final_message_separator: bool,
}

struct UserMessage {
Expand Down Expand Up @@ -649,6 +651,14 @@ impl ChatWidget {
self.flush_active_cell();

if self.stream_controller.is_none() {
if self.needs_final_message_separator {
let elapsed_seconds = self
.bottom_pane
.status_widget()
.map(super::status_indicator_widget::StatusIndicatorWidget::elapsed_seconds);
self.add_to_history(history_cell::FinalMessageSeparator::new(elapsed_seconds));
self.needs_final_message_separator = false;
}
self.stream_controller = Some(StreamController::new(self.config.clone()));
}
if let Some(controller) = self.stream_controller.as_mut()
Expand Down Expand Up @@ -902,6 +912,7 @@ impl ChatWidget {
is_review_mode: false,
ghost_snapshots: Vec::new(),
ghost_snapshots_disabled: true,
needs_final_message_separator: false,
}
}

Expand Down Expand Up @@ -963,6 +974,7 @@ impl ChatWidget {
is_review_mode: false,
ghost_snapshots: Vec::new(),
ghost_snapshots_disabled: true,
needs_final_message_separator: false,
}
}

Expand Down Expand Up @@ -1189,6 +1201,7 @@ impl ChatWidget {

fn flush_active_cell(&mut self) {
if let Some(active) = self.active_cell.take() {
self.needs_final_message_separator = true;
self.app_event_tx.send(AppEvent::InsertHistoryCell(active));
}
}
Expand All @@ -1201,6 +1214,7 @@ impl ChatWidget {
if !cell.display_lines(u16::MAX).is_empty() {
// Only break exec grouping if the cell renders visible lines.
self.flush_active_cell();
self.needs_final_message_separator = true;
}
self.app_event_tx.send(AppEvent::InsertHistoryCell(cell));
}
Expand Down Expand Up @@ -1242,6 +1256,7 @@ impl ChatWidget {
if !text.is_empty() {
self.add_to_history(history_cell::new_user_prompt(text));
}
self.needs_final_message_separator = false;
}

fn capture_ghost_snapshot(&mut self) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
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
Expand All @@ -9,6 +10,8 @@ expression: "lines[start_idx..].join(\"\\n\")"
is set up. I should look into the Cargo.toml file to confirm features and
profiles without needing to edit any code. Let's get started on this!

─ Worked for 0s ────────────────────────────────────────────────────────────────

• I’m going to scan the workspace and Cargo manifests to see build profiles and
dependencies that impact binary size. Then I’ll summarize the main causes.

Expand Down Expand Up @@ -110,6 +113,8 @@ expression: "lines[start_idx..].join(\"\\n\")"
"Main Causes" and "Build-Mode Notes." I can also include brief suggestions for
reducing size, but I want to stay focused on answering the user's question.

─ Worked for 0s ────────────────────────────────────────────────────────────────

• Here’s what’s driving size in this workspace’s binaries.

Main Causes
Expand Down
1 change: 1 addition & 0 deletions codex-rs/tui/src/chatwidget/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ fn make_chatwidget_manual() -> (
is_review_mode: false,
ghost_snapshots: Vec::new(),
ghost_snapshots_disabled: false,
needs_final_message_separator: false,
};
(widget, rx, op_rx)
}
Expand Down
34 changes: 34 additions & 0 deletions codex-rs/tui/src/history_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,40 @@ pub(crate) fn new_reasoning_summary_block(
Box::new(new_reasoning_block(full_reasoning_buffer, config))
}

#[derive(Debug)]
pub struct FinalMessageSeparator {
elapsed_seconds: Option<u64>,
}
impl FinalMessageSeparator {
pub(crate) fn new(elapsed_seconds: Option<u64>) -> Self {
Self { elapsed_seconds }
}
}
impl HistoryCell for FinalMessageSeparator {
fn display_lines(&self, width: u16) -> Vec<Line<'static>> {
let elapsed_seconds = self
.elapsed_seconds
.map(super::status_indicator_widget::fmt_elapsed_compact);
if let Some(elapsed_seconds) = elapsed_seconds {
let worked_for = format!("─ Worked for {elapsed_seconds} ─");
let worked_for_width = worked_for.width();
vec![
Line::from_iter([
worked_for,
"─".repeat((width as usize).saturating_sub(worked_for_width)),
])
.dim(),
]
} else {
vec![Line::from_iter(["─".repeat(width as usize).dim()])]
}
}

fn transcript_lines(&self) -> Vec<Line<'static>> {
vec![]
}
}

fn format_mcp_invocation<'a>(invocation: McpInvocation) -> Line<'a> {
let args_str = invocation
.arguments
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/tui/src/status_indicator_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub(crate) struct StatusIndicatorWidget {

// Format elapsed seconds into a compact human-friendly form used by the status line.
// Examples: 0s, 59s, 1m 00s, 59m 59s, 1h 00m 00s, 2h 03m 09s
fn fmt_elapsed_compact(elapsed_secs: u64) -> String {
pub fn fmt_elapsed_compact(elapsed_secs: u64) -> String {
if elapsed_secs < 60 {
return format!("{elapsed_secs}s");
}
Expand Down Expand Up @@ -142,7 +142,7 @@ impl StatusIndicatorWidget {
elapsed.as_secs()
}

fn elapsed_seconds(&self) -> u64 {
pub fn elapsed_seconds(&self) -> u64 {
self.elapsed_seconds_at(Instant::now())
}
}
Expand Down
Loading