botom pane height, restore cmd

This commit is contained in:
Rai (Michael Pokorny)
2025-06-21 03:49:22 -07:00
parent c37d82e3d5
commit bb19ef1434
6 changed files with 60 additions and 6 deletions

View File

@@ -74,7 +74,7 @@ pub enum HistoryPersistence {
}
/// Collection of settings that are specific to the TUI.
#[derive(Deserialize, Debug, Clone, PartialEq, Default)]
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct Tui {
/// By default, mouse capture is enabled in the TUI so that it is possible
/// to scroll the conversation history with a mouse. This comes at the cost
@@ -87,7 +87,27 @@ pub struct Tui {
/// the mouse is not possible, though the keyboard shortcuts e.g. `b` and
/// `space` still work. This allows the user to select text in the TUI
/// using the mouse without needing to hold down a modifier key.
#[serde(default)]
pub disable_mouse_capture: bool,
/// Maximum number of visible lines in the chat input composer before scrolling.
/// The composer will expand up to this many lines; additional content will enable
/// an internal scrollbar.
#[serde(default = "default_composer_max_rows")]
pub composer_max_rows: usize,
}
fn default_composer_max_rows() -> usize {
10
}
impl Default for Tui {
fn default() -> Self {
Self {
disable_mouse_capture: Default::default(),
composer_max_rows: default_composer_max_rows(),
}
}
}
#[derive(Deserialize, Debug, Clone, PartialEq, Default)]

View File

@@ -9,8 +9,7 @@ use crate::scroll_event_helper::ScrollEventHelper;
use crate::slash_command::SlashCommand;
use crate::tui;
use codex_core::config::Config;
use codex_core::protocol::Event;
use codex_core::protocol::Op;
use codex_core::protocol::{Event, EventMsg, Op, SessionConfiguredEvent};
use color_eyre::eyre::Result;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
@@ -20,6 +19,8 @@ use std::path::PathBuf;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::channel;
use uuid::Uuid;
/// Top-level application state: which full-screen view is currently active.
#[allow(clippy::large_enum_variant)]
enum AppState<'a> {
@@ -46,6 +47,9 @@ pub(crate) struct App<'a> {
/// Stored parameters needed to instantiate the ChatWidget later, e.g.,
/// after dismissing the Git-repo warning.
chat_args: Option<ChatWidgetArgs>,
/// Session ID reported by the backend; used for resuming the session.
session_id: Option<Uuid>,
}
/// Aggregate parameters needed to create a `ChatWidget`, as creation may be
@@ -162,6 +166,7 @@ impl<'a> App<'a> {
app_state,
config,
chat_args,
session_id: None,
}
}
@@ -171,6 +176,11 @@ impl<'a> App<'a> {
self.app_event_tx.clone()
}
/// Returns the session ID assigned by the backend for this session, if available.
pub fn session_id(&self) -> Option<Uuid> {
self.session_id
}
pub(crate) fn run(
&mut self,
terminal: &mut tui::Tui,
@@ -316,6 +326,10 @@ impl<'a> App<'a> {
}
fn dispatch_codex_event(&mut self, event: Event) {
// Capture session ID when the session is initially configured
if let EventMsg::SessionConfigured(SessionConfiguredEvent { session_id, .. }) = &event.msg {
self.session_id = Some(*session_id);
}
match &mut self.app_state {
AppState::Chat { widget } => widget.handle_codex_event(event),
AppState::Login { .. } | AppState::GitWarning { .. } => {}

View File

@@ -35,10 +35,12 @@ pub(crate) struct ChatComposer<'a> {
command_popup: Option<CommandPopup>,
app_event_tx: AppEventSender,
history: ChatComposerHistory,
/// Maximum number of visible lines in the chat input composer.
max_rows: usize,
}
impl ChatComposer<'_> {
pub fn new(has_input_focus: bool, app_event_tx: AppEventSender) -> Self {
pub fn new(has_input_focus: bool, app_event_tx: AppEventSender, max_rows: usize) -> Self {
let mut textarea = TextArea::default();
textarea.set_placeholder_text("send a message");
textarea.set_cursor_line_style(ratatui::style::Style::default());
@@ -48,6 +50,7 @@ impl ChatComposer<'_> {
command_popup: None,
app_event_tx,
history: ChatComposerHistory::new(),
max_rows,
};
this.update_border(has_input_focus);
this
@@ -249,7 +252,12 @@ impl ChatComposer<'_> {
}
pub fn calculate_required_height(&self, area: &Rect) -> u16 {
let rows = self.textarea.lines().len().max(MIN_TEXTAREA_ROWS);
let rows = self
.textarea
.lines()
.len()
.max(MIN_TEXTAREA_ROWS)
.min(self.max_rows);
let num_popup_rows = if let Some(popup) = &self.command_popup {
popup.calculate_required_height(area)
} else {

View File

@@ -41,12 +41,18 @@ pub(crate) struct BottomPane<'a> {
pub(crate) struct BottomPaneParams {
pub(crate) app_event_tx: AppEventSender,
pub(crate) has_input_focus: bool,
/// Maximum number of visible lines in the chat input composer.
pub(crate) composer_max_rows: usize,
}
impl BottomPane<'_> {
pub fn new(params: BottomPaneParams) -> Self {
Self {
composer: ChatComposer::new(params.has_input_focus, params.app_event_tx.clone()),
composer: ChatComposer::new(
params.has_input_focus,
params.app_event_tx.clone(),
params.composer_max_rows,
),
active_view: None,
app_event_tx: params.app_event_tx,
has_input_focus: params.has_input_focus,

View File

@@ -124,6 +124,7 @@ impl ChatWidget<'_> {
bottom_pane: BottomPane::new(BottomPaneParams {
app_event_tx,
has_input_focus: true,
composer_max_rows: config.tui.composer_max_rows,
}),
input_focus: InputFocus::BottomPane,
config,

View File

@@ -197,6 +197,11 @@ fn run_ratatui_app(
let app_result = app.run(&mut terminal, &mut mouse_capture);
restore();
// On exit, display a command that can be used to resume this session
#[allow(clippy::print_stderr)]
if let Some(session_id) = app.session_id() {
eprintln!("Resume this session with: codex session {session_id}");
}
app_result
}