update composer + user message styling (#4240)
Changes: - the composer and user messages now have a colored background that stretches the entire width of the terminal. - the prompt character was changed from a cyan `▌` to a bold `›`. - the "working" shimmer now follows the "dark gray" color of the terminal, better matching the terminal's color scheme | Terminal + Background | Screenshot | |------------------------------|------------| | iTerm with dark bg | <img width="810" height="641" alt="Screenshot 2025-09-25 at 11 44 52 AM" src="https://github.com/user-attachments/assets/1317e579-64a9-4785-93e6-98b0258f5d92" /> | | iTerm with light bg | <img width="845" height="540" alt="Screenshot 2025-09-25 at 11 46 29 AM" src="https://github.com/user-attachments/assets/e671d490-c747-4460-af0b-3f8d7f7a6b8e" /> | | iTerm with color bg | <img width="825" height="564" alt="Screenshot 2025-09-25 at 11 47 12 AM" src="https://github.com/user-attachments/assets/141cda1b-1164-41d5-87da-3be11e6a3063" /> | | Terminal.app with dark bg | <img width="577" height="367" alt="Screenshot 2025-09-25 at 11 45 22 AM" src="https://github.com/user-attachments/assets/93fc4781-99f7-4ee7-9c8e-3db3cd854fe5" /> | | Terminal.app with light bg | <img width="577" height="367" alt="Screenshot 2025-09-25 at 11 46 04 AM" src="https://github.com/user-attachments/assets/19bf6a3c-91e0-447b-9667-b8033f512219" /> | | Terminal.app with color bg | <img width="577" height="367" alt="Screenshot 2025-09-25 at 11 45 50 AM" src="https://github.com/user-attachments/assets/dd7c4b5b-342e-4028-8140-f4e65752bd0b" /> |
This commit is contained in:
123
codex-rs/tui/src/test_backend.rs
Normal file
123
codex-rs/tui/src/test_backend.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use std::fmt::{self};
|
||||
use std::io::Write;
|
||||
use std::io::{self};
|
||||
|
||||
use ratatui::prelude::CrosstermBackend;
|
||||
|
||||
use ratatui::backend::Backend;
|
||||
use ratatui::backend::ClearType;
|
||||
use ratatui::backend::WindowSize;
|
||||
use ratatui::buffer::Cell;
|
||||
use ratatui::layout::Position;
|
||||
use ratatui::layout::Size;
|
||||
|
||||
/// This wraps a CrosstermBackend and a vt100::Parser to mock
|
||||
/// a "real" terminal.
|
||||
///
|
||||
/// Importantly, this wrapper avoids calling any crossterm methods
|
||||
/// which write to stdout regardless of the writer. This includes:
|
||||
/// - getting the terminal size
|
||||
/// - getting the cursor position
|
||||
pub struct VT100Backend {
|
||||
crossterm_backend: CrosstermBackend<vt100::Parser>,
|
||||
}
|
||||
|
||||
impl VT100Backend {
|
||||
/// Creates a new `TestBackend` with the specified width and height.
|
||||
pub fn new(width: u16, height: u16) -> Self {
|
||||
Self {
|
||||
crossterm_backend: CrosstermBackend::new(vt100::Parser::new(height, width, 0)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vt100(&self) -> &vt100::Parser {
|
||||
self.crossterm_backend.writer()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for VT100Backend {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.crossterm_backend.writer_mut().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.crossterm_backend.writer_mut().flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VT100Backend {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.crossterm_backend.writer().screen().contents())
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for VT100Backend {
|
||||
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
|
||||
where
|
||||
I: Iterator<Item = (u16, u16, &'a Cell)>,
|
||||
{
|
||||
self.crossterm_backend.draw(content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn hide_cursor(&mut self) -> io::Result<()> {
|
||||
self.crossterm_backend.hide_cursor()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_cursor(&mut self) -> io::Result<()> {
|
||||
self.crossterm_backend.show_cursor()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_cursor_position(&mut self) -> io::Result<Position> {
|
||||
Ok(self.vt100().screen().cursor_position().into())
|
||||
}
|
||||
|
||||
fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> io::Result<()> {
|
||||
self.crossterm_backend.set_cursor_position(position)
|
||||
}
|
||||
|
||||
fn clear(&mut self) -> io::Result<()> {
|
||||
self.crossterm_backend.clear()
|
||||
}
|
||||
|
||||
fn clear_region(&mut self, clear_type: ClearType) -> io::Result<()> {
|
||||
self.crossterm_backend.clear_region(clear_type)
|
||||
}
|
||||
|
||||
fn append_lines(&mut self, line_count: u16) -> io::Result<()> {
|
||||
self.crossterm_backend.append_lines(line_count)
|
||||
}
|
||||
|
||||
fn size(&self) -> io::Result<Size> {
|
||||
Ok(self.vt100().screen().size().into())
|
||||
}
|
||||
|
||||
fn window_size(&mut self) -> io::Result<WindowSize> {
|
||||
Ok(WindowSize {
|
||||
columns_rows: self.vt100().screen().size().into(),
|
||||
// Arbitrary size, we don't rely on this in testing.
|
||||
pixels: Size {
|
||||
width: 640,
|
||||
height: 480,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.crossterm_backend.writer_mut().flush()
|
||||
}
|
||||
|
||||
fn scroll_region_up(&mut self, region: std::ops::Range<u16>, scroll_by: u16) -> io::Result<()> {
|
||||
self.crossterm_backend.scroll_region_up(region, scroll_by)
|
||||
}
|
||||
|
||||
fn scroll_region_down(
|
||||
&mut self,
|
||||
region: std::ops::Range<u16>,
|
||||
scroll_by: u16,
|
||||
) -> io::Result<()> {
|
||||
self.crossterm_backend.scroll_region_down(region, scroll_by)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user