TUI tweaks (#2685)

* Clear builds logs on clear screen key event and fix keybinds rendering
* Clear the currently selected build logs
* Add placeholder for / screen
* Change tabs to be 1-indexed
* Redirect build errors to build tab
* Open the running app with 'o'
This commit is contained in:
Matt Hunzinger authored and GitHub committed 2024-07-26 17:12:54 -07:00
1 parent 1a66987245
commit c48232f386
2 files changed
+63 -33

No files matched your search

+10 -3
View File
@@ -67,7 +67,7 @@ pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
// rebuild the project or hotreload it // rebuild the project or hotreload it
_ = watcher.wait() => { _ = watcher.wait() => {
if !watcher.pending_changes() { if !watcher.pending_changes() {
continue; continue
} }
let changed_files = watcher.dequeue_changed_files(&dioxus_crate); let changed_files = watcher.dequeue_changed_files(&dioxus_crate);
@@ -149,8 +149,15 @@ pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
// Handle input from the user using our settings // Handle input from the user using our settings
res = screen.wait() => { res = screen.wait() => {
if res.is_err() { match res {
break; Ok(false) => {}
// Request a rebuild.
Ok(true) => {
builder.build();
server.start_build().await
},
// Shutdown the server.
Err(_) => break,
} }
} }
} }
+53 -30
View File
@@ -13,7 +13,7 @@ use crossterm::{
tty::IsTty, tty::IsTty,
ExecutableCommand, ExecutableCommand,
}; };
use dioxus_cli_config::Platform; use dioxus_cli_config::{AddressArguments, Platform};
use dioxus_hot_reload::ClientMsg; use dioxus_hot_reload::ClientMsg;
use futures_util::{future::select_all, Future, StreamExt}; use futures_util::{future::select_all, Future, StreamExt};
use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport}; use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport};
@@ -75,6 +75,8 @@ pub struct Output {
anim_start: Instant, anim_start: Instant,
tab: Tab, tab: Tab,
addr: AddressArguments,
} }
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]
@@ -151,6 +153,7 @@ impl Output {
num_lines_with_wrapping: 0, num_lines_with_wrapping: 0,
anim_start: Instant::now(), anim_start: Instant::now(),
tab: Tab::BuildLog, tab: Tab::BuildLog,
addr: cfg.server_arguments.address.clone(),
}) })
} }
@@ -159,7 +162,7 @@ impl Output {
/// Why is the ctrl_c handler here? /// Why is the ctrl_c handler here?
/// ///
/// Also tick animations every few ms /// Also tick animations every few ms
pub async fn wait(&mut self) -> io::Result<()> { pub async fn wait(&mut self) -> io::Result<bool> {
// sorry lord // sorry lord
let user_input = match self.events.as_mut() { let user_input = match self.events.as_mut() {
Some(events) => { Some(events) => {
@@ -208,24 +211,28 @@ impl Output {
}) })
} }
if let Some(stderr) = stderr { if let Some(stderr) = stderr {
self.set_tab(Tab::BuildLog);
self.running_apps.get_mut(&platform).unwrap().stdout.as_mut().unwrap().stderr_line.push_str(&stderr); self.running_apps.get_mut(&platform).unwrap().stdout.as_mut().unwrap().stderr_line.push_str(&stderr);
self.push_log(platform, BuildMessage { self.build_progress.build_logs.get_mut(&platform).unwrap().messages.push(BuildMessage {
level: Level::ERROR, level: Level::ERROR,
message: MessageType::Text(stderr), message: MessageType::Text(stderr),
source: Some("app".to_string()), source: Some("app".to_string()),
}) });
} }
}, },
event = user_input => { event = user_input => {
self.handle_events(event.unwrap().unwrap()).await?; if self.handle_events(event.unwrap().unwrap()).await? {
return Ok(true)
}
// self.handle_input(event.unwrap().unwrap())?; // self.handle_input(event.unwrap().unwrap())?;
} }
_ = animation_timeout => {} _ = animation_timeout => {}
} }
Ok(()) Ok(false)
} }
pub fn shutdown(&mut self) -> io::Result<()> { pub fn shutdown(&mut self) -> io::Result<()> {
@@ -267,7 +274,8 @@ impl Output {
} }
} }
pub fn handle_input(&mut self, input: Event) -> io::Result<()> { /// Handle an input event, returning `true` if the event should cause the program to restart.
pub fn handle_input(&mut self, input: Event) -> io::Result<bool> {
// handle ctrlc // handle ctrlc
if let Event::Key(key) = input { if let Event::Key(key) = input {
if let KeyCode::Char('c') = key.code { if let KeyCode::Char('c') = key.code {
@@ -296,21 +304,29 @@ impl Output {
Event::Key(key) if key.code == KeyCode::Down => { Event::Key(key) if key.code == KeyCode::Down => {
self.scroll += 1; self.scroll += 1;
} }
Event::Key(key) if key.code == KeyCode::Char('r') => {} Event::Key(key) if key.code == KeyCode::Char('r') => {
// todo: reload the app
return Ok(true);
}
Event::Key(key) if key.code == KeyCode::Char('o') => { Event::Key(key) if key.code == KeyCode::Char('o') => {
// todo: open the app // Open the running app.
open::that(format!("http://{}:{}", self.addr.addr, self.addr.port))?;
} }
Event::Key(key) if key.code == KeyCode::Char('c') => { Event::Key(key) if key.code == KeyCode::Char('c') => {
// clear // Clear the currently selected build logs.
} let build = self
Event::Key(key) if key.code == KeyCode::Char('0') => { .build_progress
self.tab = Tab::Console; .build_logs
self.scroll = 0; .get_mut(&self.platform)
} .unwrap();
Event::Key(key) if key.code == KeyCode::Char('1') => { let msgs = match self.tab {
self.tab = Tab::BuildLog; Tab::Console => &mut build.stdout_logs,
self.scroll = 0; Tab::BuildLog => &mut build.messages,
};
msgs.clear();
} }
Event::Key(key) if key.code == KeyCode::Char('1') => self.set_tab(Tab::Console),
Event::Key(key) if key.code == KeyCode::Char('2') => self.set_tab(Tab::BuildLog),
Event::Resize(_width, _height) => { Event::Resize(_width, _height) => {
// nothing, it should take care of itself // nothing, it should take care of itself
} }
@@ -327,7 +343,7 @@ impl Output {
.saturating_sub(self.term_height + 1); .saturating_sub(self.term_height + 1);
} }
Ok(()) Ok(false)
} }
pub fn new_ws_message(&mut self, platform: Platform, message: axum::extract::ws::Message) { pub fn new_ws_message(&mut self, platform: Platform, message: axum::extract::ws::Message) {
@@ -495,7 +511,8 @@ impl Output {
.constraints([Constraint::Fill(1), Constraint::Length(14)].as_ref()) .constraints([Constraint::Fill(1), Constraint::Length(14)].as_ref())
.split(body[1]); .split(body[1]);
let listening_len = "listening at http://127.0.0.1:8080".len() + 3; let addr = format!("http://{}:{}", self.addr.addr, self.addr.port);
let listening_len = format!("listening at {addr}").len() + 3;
let listening_len = if listening_len > body[0].width as usize { let listening_len = if listening_len > body[0].width as usize {
0 0
} else { } else {
@@ -571,14 +588,14 @@ impl Output {
frame.render_widget( frame.render_widget(
Paragraph::new(vec![ Paragraph::new(vec![
{ {
let mut line = Line::from(" [0] console").dark_gray(); let mut line = Line::from(" [1] console").dark_gray();
if self.tab == Tab::Console { if self.tab == Tab::Console {
line.style = Style::default().fg(Color::LightYellow); line.style = Style::default().fg(Color::LightYellow);
} }
line line
}, },
{ {
let mut line = Line::from(" [1] build").dark_gray(); let mut line = Line::from(" [2] build").dark_gray();
if self.tab == Tab::BuildLog { if self.tab == Tab::BuildLog {
line.style = Style::default().fg(Color::LightYellow); line.style = Style::default().fg(Color::LightYellow);
} }
@@ -587,7 +604,7 @@ impl Output {
Line::from(" ").gray(), Line::from(" ").gray(),
Line::from(" [/] more").gray(), Line::from(" [/] more").gray(),
Line::from(" [r] reload").gray(), Line::from(" [r] reload").gray(),
Line::from(" [r] clear").gray(), Line::from(" [c] clear").gray(),
Line::from(" [o] open").gray(), Line::from(" [o] open").gray(),
Line::from(" [h] hide").gray(), Line::from(" [h] hide").gray(),
]) ])
@@ -683,7 +700,7 @@ impl Output {
}); });
} }
async fn handle_events(&mut self, event: Event) -> io::Result<()> { async fn handle_events(&mut self, event: Event) -> io::Result<bool> {
let mut events = vec![event]; let mut events = vec![event];
// Collect all the events within the next 10ms in one stream // Collect all the events within the next 10ms in one stream
@@ -699,12 +716,15 @@ impl Output {
let mut handled = HashSet::new(); let mut handled = HashSet::new();
for event in events { for event in events {
if !handled.contains(&event) { if !handled.contains(&event) {
self.handle_input(event.clone())?; if self.handle_input(event.clone())? {
// Restart the running app.
return Ok(true);
}
handled.insert(event); handled.insert(event);
} }
} }
Ok(()) Ok(false)
} }
fn render_fly_modal(&mut self, frame: &mut Frame, area: Rect) { fn render_fly_modal(&mut self, frame: &mut Frame, area: Rect) {
@@ -722,12 +742,15 @@ impl Output {
frame.render_widget(Clear, panel); frame.render_widget(Clear, panel);
frame.render_widget(Block::default().borders(Borders::ALL), panel); frame.render_widget(Block::default().borders(Borders::ALL), panel);
let modal = Paragraph::new( let modal = Paragraph::new("Under construction, please check back at a later date!\n")
"Hello world!\nHello world!\nHello world!\nHello world!\nHello world!\n", .alignment(Alignment::Center);
)
.alignment(Alignment::Center);
frame.render_widget(modal, panel); frame.render_widget(modal, panel);
} }
fn set_tab(&mut self, tab: Tab) {
self.tab = tab;
self.scroll = 0;
}
} }
#[derive(Default, Debug, PartialEq)] #[derive(Default, Debug, PartialEq)]