Skip to content
Open
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
29 changes: 29 additions & 0 deletions codex-rs/tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ pub(crate) struct App<'a> {
pending_history_lines: Vec<Line<'static>>,

enhanced_keys_supported: bool,

// UI options passed from CLI
live_rows: u16,
overlay_wrap: bool,
preview_max_cols: Option<u16>,
}

/// Aggregate parameters needed to create a `ChatWidget`, as creation may be
Expand All @@ -74,6 +79,9 @@ pub(crate) struct ChatWidgetArgs {
initial_prompt: Option<String>,
initial_images: Vec<PathBuf>,
enhanced_keys_supported: bool,
live_rows: u16,
overlay_wrap: bool,
preview_max_cols: Option<u16>,
}

impl App<'_> {
Expand All @@ -82,6 +90,9 @@ impl App<'_> {
initial_prompt: Option<String>,
initial_images: Vec<std::path::PathBuf>,
show_trust_screen: bool,
live_rows: u16,
overlay_wrap: bool,
preview_max_cols: Option<u16>,
) -> Self {
let (app_event_tx, app_event_rx) = channel();
let app_event_tx = AppEventSender::new(app_event_tx);
Expand Down Expand Up @@ -139,6 +150,9 @@ impl App<'_> {
initial_prompt,
initial_images,
enhanced_keys_supported,
live_rows,
overlay_wrap,
preview_max_cols,
};
AppState::Onboarding {
screen: OnboardingScreen::new(OnboardingScreenArgs {
Expand All @@ -157,6 +171,9 @@ impl App<'_> {
initial_prompt,
initial_images,
enhanced_keys_supported,
live_rows,
overlay_wrap,
preview_max_cols,
);
AppState::Chat {
widget: Box::new(chat_widget),
Expand All @@ -173,6 +190,9 @@ impl App<'_> {
file_search,
pending_redraw,
enhanced_keys_supported,
live_rows,
overlay_wrap,
preview_max_cols,
}
}

Expand Down Expand Up @@ -319,6 +339,9 @@ impl App<'_> {
None,
Vec::new(),
self.enhanced_keys_supported,
self.live_rows,
self.overlay_wrap,
self.preview_max_cols,
));
self.app_state = AppState::Chat { widget: new_widget };
self.app_event_tx.send(AppEvent::RequestRedraw);
Expand Down Expand Up @@ -426,6 +449,9 @@ impl App<'_> {
enhanced_keys_supported,
initial_images,
initial_prompt,
live_rows,
overlay_wrap,
preview_max_cols,
}) => {
self.app_state = AppState::Chat {
widget: Box::new(ChatWidget::new(
Expand All @@ -434,6 +460,9 @@ impl App<'_> {
initial_prompt,
initial_images,
enhanced_keys_supported,
live_rows,
overlay_wrap,
preview_max_cols,
)),
}
}
Expand Down
1 change: 1 addition & 0 deletions codex-rs/tui/src/bottom_pane/approval_modal_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ mod tests {
app_event_tx: AppEventSender::new(tx_raw2),
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});
assert_eq!(CancellationEvent::Handled, view.on_ctrl_c(&mut pane));
assert!(view.queue.is_empty());
Expand Down
11 changes: 10 additions & 1 deletion codex-rs/tui/src/bottom_pane/live_ring_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ use ratatui::widgets::WidgetRef;
pub(crate) struct LiveRingWidget {
max_rows: u16,
rows: Vec<Line<'static>>, // newest at the end
wrap: bool,
}

impl LiveRingWidget {
pub fn new() -> Self {
Self {
max_rows: 3,
rows: Vec::new(),
wrap: false,
}
}

Expand All @@ -26,6 +28,10 @@ impl LiveRingWidget {
self.rows = rows;
}

pub fn set_wrap(&mut self, wrap: bool) {
self.wrap = wrap;
}

pub fn desired_height(&self, _width: u16) -> u16 {
let len = self.rows.len() as u16;
len.min(self.max_rows)
Expand All @@ -39,7 +45,10 @@ impl WidgetRef for LiveRingWidget {
}
let visible = self.rows.len().saturating_sub(self.max_rows as usize);
let slice = &self.rows[visible..];
let para = Paragraph::new(slice.to_vec());
let mut para = Paragraph::new(slice.to_vec());
if self.wrap {
para = para.wrap(ratatui::widgets::Wrap { trim: false });
}
para.render_ref(area, buf);
}
}
14 changes: 14 additions & 0 deletions codex-rs/tui/src/bottom_pane/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ pub(crate) struct BottomPane<'a> {
/// container used during development before we wire it to ChatWidget events.
live_ring: Option<live_ring_widget::LiveRingWidget>,

/// Whether the live ring should soft-wrap to the terminal width.
live_ring_wrap: bool,

/// True if the active view is the StatusIndicatorView that replaces the
/// composer during a running task.
status_view_active: bool,
Expand All @@ -70,6 +73,7 @@ pub(crate) struct BottomPaneParams {
pub(crate) app_event_tx: AppEventSender,
pub(crate) has_input_focus: bool,
pub(crate) enhanced_keys_supported: bool,
pub(crate) live_ring_wrap: bool,
}

impl BottomPane<'_> {
Expand All @@ -89,6 +93,7 @@ impl BottomPane<'_> {
ctrl_c_quit_hint: false,
live_status: None,
live_ring: None,
live_ring_wrap: params.live_ring_wrap,
status_view_active: false,
}
}
Expand Down Expand Up @@ -357,6 +362,7 @@ impl BottomPane<'_> {
pub(crate) fn set_live_ring_rows(&mut self, max_rows: u16, rows: Vec<Line<'static>>) {
let mut w = live_ring_widget::LiveRingWidget::new();
w.set_max_rows(max_rows);
w.set_wrap(self.live_ring_wrap);
w.set_rows(rows);
self.live_ring = Some(w);
}
Expand Down Expand Up @@ -459,6 +465,7 @@ mod tests {
app_event_tx: tx,
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});
pane.push_approval_request(exec_request());
assert_eq!(CancellationEvent::Handled, pane.on_ctrl_c());
Expand All @@ -474,6 +481,7 @@ mod tests {
app_event_tx: tx,
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});

// Provide 4 rows with max_rows=3; only the last 3 should be visible.
Expand Down Expand Up @@ -511,6 +519,7 @@ mod tests {
app_event_tx: tx,
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});

// Simulate task running which replaces composer with the status indicator.
Expand Down Expand Up @@ -572,6 +581,7 @@ mod tests {
app_event_tx: tx,
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});

// Create an approval modal (active view).
Expand Down Expand Up @@ -602,6 +612,7 @@ mod tests {
app_event_tx: tx.clone(),
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});

// Start a running task so the status indicator replaces the composer.
Expand Down Expand Up @@ -652,6 +663,7 @@ mod tests {
app_event_tx: tx,
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});

// Begin a task: show initial status.
Expand Down Expand Up @@ -688,6 +700,7 @@ mod tests {
app_event_tx: tx,
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});

// Activate spinner (status view replaces composer) with no live ring.
Expand Down Expand Up @@ -740,6 +753,7 @@ mod tests {
app_event_tx: tx,
has_input_focus: true,
enhanced_keys_supported: false,
live_ring_wrap: false,
});

pane.set_task_running(true);
Expand Down
Loading