mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Render the initial TUI session header before input (#38639)
## What changed - Drain queued app events until the initial session header is installed before handling terminal input. - Render the active session header without a leading separator so it starts at the top of the viewport. ## Testing - Add a rendering snapshot that verifies the initial header begins on the first viewport row. GitOrigin-RevId: 070417776b629c681b3f94e93ba619dfefe764b1
This commit is contained in:
+16
-2
@@ -1181,6 +1181,7 @@ See the Codex keymap documentation for supported actions and examples."
|
||||
|
||||
let mut listen_for_app_server_events = true;
|
||||
let mut waiting_for_initial_session_configured = wait_for_initial_session_configured;
|
||||
let mut waiting_for_initial_session_header = true;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
let pre_loop_exit_reason = if let Some(latest_version) = upgrade_version {
|
||||
@@ -1207,10 +1208,23 @@ See the Codex keymap documentation for supported actions and examples."
|
||||
Ok(exit_reason)
|
||||
} else {
|
||||
loop {
|
||||
let initial_session_header_pending = waiting_for_initial_session_header
|
||||
&& app.primary_session_configured.is_some()
|
||||
&& !app_event_rx.is_empty();
|
||||
let control = select! {
|
||||
Some(event) = app_event_rx.recv() => {
|
||||
let is_initial_session_header = matches!(
|
||||
&event,
|
||||
AppEvent::InsertHistoryCell(cell)
|
||||
if cell.as_any().is::<history_cell::SessionInfoCell>()
|
||||
);
|
||||
match Box::pin(app.handle_event(tui, &mut app_server, event)).await {
|
||||
Ok(control) => control,
|
||||
Ok(control) => {
|
||||
if is_initial_session_header {
|
||||
waiting_for_initial_session_header = false;
|
||||
}
|
||||
control
|
||||
}
|
||||
Err(err) => break Err(err),
|
||||
}
|
||||
}
|
||||
@@ -1233,7 +1247,7 @@ See the Codex keymap documentation for supported actions and examples."
|
||||
}
|
||||
AppRunControl::Continue
|
||||
}
|
||||
event = tui_events.next() => {
|
||||
event = tui_events.next(), if !initial_session_header_pending => {
|
||||
if let Some(event) = event {
|
||||
match app.handle_tui_event(tui, &mut app_server, event).await {
|
||||
Ok(control) => control,
|
||||
|
||||
@@ -11,7 +11,12 @@ impl ChatWidget {
|
||||
let active_cell_renderable = match &self.transcript.active_cell {
|
||||
Some(cell) => RenderableItem::Owned(Box::new(TranscriptAreaRenderable {
|
||||
child: cell.as_ref(),
|
||||
top: 1,
|
||||
// The initial header becomes the first history cell, which has no leading separator.
|
||||
top: if cell.as_any().is::<history_cell::SessionHeaderHistoryCell>() {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
},
|
||||
right: active_cell_right_reserve,
|
||||
// Externally backed transcript cells can also change viewport height without an
|
||||
// active-cell revision. Spinner cells remain safe because their indicator width
|
||||
|
||||
@@ -75,6 +75,41 @@ fn contains_text(buffer: &Buffer, text: &str) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn initial_session_header_starts_at_the_top_of_the_viewport() {
|
||||
let (mut widget, _sender, _events, _operations) = make_chatwidget_manual_with_sender().await;
|
||||
widget.transcript.active_cell =
|
||||
Some(ChatWidget::placeholder_session_header_cell(&widget.config));
|
||||
|
||||
let frame = render_frame(&widget, /*width*/ 48);
|
||||
let header = frame
|
||||
.content
|
||||
.chunks(usize::from(frame.area.width))
|
||||
.take(/*n*/ 6)
|
||||
.map(|row| {
|
||||
row.iter()
|
||||
.map(ratatui::buffer::Cell::symbol)
|
||||
.collect::<String>()
|
||||
.trim_end()
|
||||
.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.replace(crate::version::CODEX_CLI_VERSION, "<VERSION>");
|
||||
|
||||
let cwd = widget.config.cwd.as_path().display().to_string();
|
||||
let normalized_cwd = format!("{:<width$}", "/tmp/project", width = cwd.len());
|
||||
|
||||
insta::assert_snapshot!(header.replace(&cwd, &normalized_cwd), @r"
|
||||
╭───────────────────────────────────────╮
|
||||
│ >_ OpenAI Codex (v<VERSION>) │
|
||||
│ │
|
||||
│ model: loading /model to change │
|
||||
│ directory: /tmp/project │
|
||||
╰───────────────────────────────────────╯
|
||||
");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn active_cell_layout_reuses_heights_without_freezing_animation() {
|
||||
let (widget, desired_height_calls, display_lines_calls) = widget_with_counting_cell(
|
||||
|
||||
Reference in New Issue
Block a user