streamline ui (#1733)

Simplify and improve many UI elements.
* Remove all-around borders in most places. These interact badly with
terminal resizing and look heavy. Prefer left-side-only borders.
* Make the viewport adjust to the size of its contents.
* <kbd>/</kbd> and <kbd>@</kbd> autocomplete boxes appear below the
prompt, instead of above it.
* Restyle the keyboard shortcut hints & move them to the left.
* Restyle the approval dialog.
* Use synchronized rendering to avoid flashing during rerenders.


https://github.com/user-attachments/assets/96f044af-283b-411c-b7fc-5e6b8a433c20

<img width="1117" height="858" alt="Screenshot 2025-07-30 at 5 29 20 PM"
src="https://github.com/user-attachments/assets/0cc0af77-8396-429b-b6ee-9feaaccdbee7"
/>
This commit is contained in:
Jeremy Rose
2025-07-31 00:43:21 -07:00
committed by GitHub
parent defeafb279
commit d86270696e
18 changed files with 232 additions and 173 deletions

View File

@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
@@ -44,6 +45,12 @@ use crate::history_cell::PatchEventType;
use crate::user_approval_widget::ApprovalRequest;
use codex_file_search::FileMatch;
struct RunningCommand {
command: Vec<String>,
#[allow(dead_code)]
cwd: PathBuf,
}
pub(crate) struct ChatWidget<'a> {
app_event_tx: AppEventSender,
codex_op_tx: UnboundedSender<Op>,
@@ -56,6 +63,7 @@ pub(crate) struct ChatWidget<'a> {
// We wait for the final AgentMessage event and then emit the full text
// at once into scrollback so the history contains a single message.
answer_buffer: String,
running_commands: HashMap<String, RunningCommand>,
}
struct UserMessage {
@@ -140,11 +148,12 @@ impl ChatWidget<'_> {
token_usage: TokenUsage::default(),
reasoning_buffer: String::new(),
answer_buffer: String::new(),
running_commands: HashMap::new(),
}
}
pub fn desired_height(&self) -> u16 {
self.bottom_pane.desired_height()
pub fn desired_height(&self, width: u16) -> u16 {
self.bottom_pane.desired_height(width)
}
pub(crate) fn handle_key_event(&mut self, key_event: KeyEvent) {
@@ -343,12 +352,18 @@ impl ChatWidget<'_> {
self.request_redraw();
}
EventMsg::ExecCommandBegin(ExecCommandBeginEvent {
call_id: _,
call_id,
command,
cwd: _,
cwd,
}) => {
self.running_commands.insert(
call_id,
RunningCommand {
command: command.clone(),
cwd: cwd.clone(),
},
);
self.add_to_history(HistoryCell::new_active_exec_command(command));
self.request_redraw();
}
EventMsg::PatchApplyBegin(PatchApplyBeginEvent {
call_id: _,
@@ -361,7 +376,6 @@ impl ChatWidget<'_> {
PatchEventType::ApplyBegin { auto_approved },
changes,
));
self.request_redraw();
}
EventMsg::ExecCommandEnd(ExecCommandEndEvent {
call_id,
@@ -369,8 +383,9 @@ impl ChatWidget<'_> {
stdout,
stderr,
}) => {
let cmd = self.running_commands.remove(&call_id);
self.add_to_history(HistoryCell::new_completed_exec_command(
call_id,
cmd.map(|cmd| cmd.command).unwrap_or_else(|| vec![call_id]),
CommandOutput {
exit_code,
stdout,
@@ -384,7 +399,6 @@ impl ChatWidget<'_> {
invocation,
}) => {
self.add_to_history(HistoryCell::new_active_mcp_tool_call(invocation));
self.request_redraw();
}
EventMsg::McpToolCallEnd(McpToolCallEndEvent {
call_id: _,
@@ -419,7 +433,6 @@ impl ChatWidget<'_> {
}
event => {
self.add_to_history(HistoryCell::new_background_event(format!("{event:?}")));
self.request_redraw();
}
}
}
@@ -436,7 +449,6 @@ impl ChatWidget<'_> {
pub(crate) fn add_diff_output(&mut self, diff_output: String) {
self.add_to_history(HistoryCell::new_diff_output(diff_output.clone()));
self.request_redraw();
}
/// Forward file-search results to the bottom pane.