Skip to content

Easily Selectable History#1672

Merged
easong-openai merged 7 commits intomainfrom
eason-selectable-history
Jul 25, 2025
Merged

Easily Selectable History#1672
easong-openai merged 7 commits intomainfrom
eason-selectable-history

Conversation

@easong-openai
Copy link
Copy Markdown
Collaborator

This update replaces the previous ratatui history widget with an append-only log so that the terminal can handle text selection and scrolling. It also disables streaming responses, which we'll do our best to bring back in a later PR. It also adds a small summary of token use after the TUI exits.

Copy link
Copy Markdown
Collaborator

@bolinfest bolinfest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this is really great!

Unsurprisingly, the implementation of insert_history_lines() is what gives me the most pause. I haven't played with these APIs/edge cases myself, so I assume all the complexity is necessary to make things work.

It would certainly be helpful to add unit tests in the near future to be sure that we are catching all these edge cases.

Comment thread codex-rs/tui/src/app_event.rs Outdated

/// Append immutable history lines above the inline viewport. Part of the
/// incremental migration to the hybrid scrollback model described in
/// `fix-history-plan.md`.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reference to fix-history-plan.md is outdated now?

token_usage: TokenUsage,
reasoning_buffer: String,
answer_buffer: String,
// Buffer for streaming assistant answer text; we do not surface partial
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this comment should be "attached" to fn or struct?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note doc comments in Rust should have ///.

Comment thread codex-rs/tui/src/chatwidget.rs
Comment thread codex-rs/tui/src/chatwidget.rs Outdated
Comment on lines +225 to +227
if let Some(lines) = self.conversation_history.last_entry_plain_lines() {
self.app_event_tx.send(AppEvent::InsertHistory(lines));
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to happen enough that it feels like it should be a helper function.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

| HistoryCell::PendingPatch { view }
| HistoryCell::ActiveExecCommand { view, .. }
| HistoryCell::ActiveMcpToolCall { view, .. } => view.lines.clone(),
HistoryCell::CompletedMcpToolCallWithImageOutput { .. } => vec![
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the most high priority thing, but can we not support images anymore?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will support images, but not immediately (which I think is the same as the current UX?)

Comment thread codex-rs/tui/src/insert_history.rs Outdated
@@ -0,0 +1,183 @@
use crate::tui; // for the Tui type alias
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop comment?

Comment thread codex-rs/tui/src/lib.rs
pub fn run_main(
cli: Cli,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> std::io::Result<codex_core::protocol::TokenUsage> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be helpful to introduce a struct like FinalOutput that has TokenUsage (and maybe final_message: Option<String>) as a field.

Then I would have it impl Display for FinalOutput with the implementation that corresponds to this code:

            println!(
                "Token usage: total={} input={}{} output={}{}",
                usage.total_tokens,
                usage.input_tokens,
                usage
                    .cached_input_tokens
                    .map(|c| format!(" (cached {c})"))
                    .unwrap_or_default(),
                usage.output_tokens,
                usage
                    .reasoning_output_tokens
                    .map(|r| format!(" (reasoning {r})"))
                    .unwrap_or_default()
            );

so that you can just do:

println!("{final_output}");

rather than copy/paste the two implementations.

pub fn description(self) -> &'static str {
match self {
SlashCommand::New => "Start a new chat.",
SlashCommand::ToggleMouseMode => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! We can also remove disable_mouse_capture in the docs (config.md) and Config code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

/// `ConversationHistory`. They may contain embedded newlines and arbitrary
/// runs of whitespace inside individual [`Span`]s. All of that must be
/// normalised before writing to the backing terminal buffer because the
/// ratatui [`Paragraph`] widget does not perform soft‑wrapping when used in
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So even if we explicitly use the wrap() method of Paragraph, there is nothing we can do?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I intend to refactor this code significantly to try to support live updates before committing a final version (which would allow us to bring back streaming/command outputs).

Comment thread codex-rs/tui/src/insert_history.rs Outdated
let style = span.style;
let mut buf_word = String::new();
let mut buf_space = String::new();
let flush_word = |word: &mut String,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we pull out the appropriate arguments so this can be a top-level function instead of a closure? I think that would make things a bit easier to follow.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, maybe it would be easier to create a struct with some methods on it to manage this entire computation? Then things can be read off self instead of passed in?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Struct makes sense to me!

Copy link
Copy Markdown
Collaborator

@bolinfest bolinfest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few small suggestions: great work!

use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::str::FromStr; // Added for FinalOutput Display implementation
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove comment?

pub total_tokens: u64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protocol.rs should only have things that are part of the protocol between the business logic and the UI layer. This seems more appropriate for codex-rs/common?

token_usage: TokenUsage,
reasoning_buffer: String,
answer_buffer: String,
// Buffer for streaming assistant answer text; we do not surface partial
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note doc comments in Rust should have ///.

@easong-openai easong-openai merged commit 480e82b into main Jul 25, 2025
11 checks passed
@easong-openai easong-openai deleted the eason-selectable-history branch July 25, 2025 08:56
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 25, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants