From 73322138c12a24a827ccc459cd9cf4373b98e27a Mon Sep 17 00:00:00 2001 From: dailz Date: Fri, 10 Apr 2026 21:20:14 +0800 Subject: [PATCH] feat(tui): ratatui skeleton with layout Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- crates/tui/src/app.rs | 17 +++++++++++++++++ crates/tui/src/main.rs | 42 +++++++++++++++++++++++++++++++++++++++++- crates/tui/src/ui.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 crates/tui/src/app.rs create mode 100644 crates/tui/src/ui.rs diff --git a/crates/tui/src/app.rs b/crates/tui/src/app.rs new file mode 100644 index 0000000..1699767 --- /dev/null +++ b/crates/tui/src/app.rs @@ -0,0 +1,17 @@ +pub struct App { + pub should_quit: bool, +} + +impl App { + pub fn new() -> Self { + Self { should_quit: false } + } + + pub fn handle_key(&mut self, key: crossterm::event::KeyEvent) { + match key.code { + crossterm::event::KeyCode::Char('q') => self.should_quit = true, + crossterm::event::KeyCode::Esc => self.should_quit = true, + _ => {} + } + } +} diff --git a/crates/tui/src/main.rs b/crates/tui/src/main.rs index f328e4d..a021895 100644 --- a/crates/tui/src/main.rs +++ b/crates/tui/src/main.rs @@ -1 +1,41 @@ -fn main() {} +use clap::Parser; + +mod app; +mod ui; + +#[derive(Parser)] +#[command(name = "log-viewer", about = "A log viewer TUI")] +struct Cli { + /// Log files to open + files: Vec, +} + +fn main() -> anyhow::Result<()> { + let _cli = Cli::parse(); + + crossterm::terminal::enable_raw_mode()?; + let mut stdout = std::io::stdout(); + crossterm::execute!(stdout, crossterm::terminal::EnterAlternateScreen)?; + let backend = ratatui::backend::CrosstermBackend::new(stdout); + let mut terminal = ratatui::Terminal::new(backend)?; + + let mut app = app::App::new(); + + while !app.should_quit { + terminal.draw(|frame| ui::render(frame, &app))?; + if crossterm::event::poll(std::time::Duration::from_millis(100))? + && let crossterm::event::Event::Key(key) = crossterm::event::read()? + { + app.handle_key(key); + } + } + + crossterm::terminal::disable_raw_mode()?; + crossterm::execute!( + terminal.backend_mut(), + crossterm::terminal::LeaveAlternateScreen + )?; + terminal.show_cursor()?; + + Ok(()) +} diff --git a/crates/tui/src/ui.rs b/crates/tui/src/ui.rs new file mode 100644 index 0000000..e44685c --- /dev/null +++ b/crates/tui/src/ui.rs @@ -0,0 +1,26 @@ +use crate::app::App; + +pub fn render(frame: &mut ratatui::Frame, _app: &App) { + use ratatui::layout::{Constraint, Layout}; + use ratatui::widgets::{Block, Borders, Paragraph}; + + let chunks = Layout::vertical([ + Constraint::Length(1), + Constraint::Min(1), + Constraint::Length(1), + ]) + .split(frame.area()); + + frame.render_widget( + Paragraph::new(" Log Viewer").style(ratatui::style::Style::default().bold()), + chunks[0], + ); + + // Main area — 使用 Block::new()(ratatui 0.30 推荐风格) + frame.render_widget( + Block::new().borders(Borders::ALL).title("No file loaded"), + chunks[1], + ); + + frame.render_widget(Paragraph::new(" Press '?' for help | q to quit"), chunks[2]); +}