feat(tui): ratatui skeleton with layout
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
17
crates/tui/src/app.rs
Normal file
17
crates/tui/src/app.rs
Normal file
@@ -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,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|||||||
26
crates/tui/src/ui.rs
Normal file
26
crates/tui/src/ui.rs
Normal file
@@ -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]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user