From 571b719a263026bc8f46a9afb29b9df73e82adb3 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Sun, 24 Nov 2024 13:30:21 -0700 Subject: [PATCH 01/22] refactor(tui): move vim keybinds for task list in handle list. --- crates/turborepo-ui/src/tui/input.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/turborepo-ui/src/tui/input.rs b/crates/turborepo-ui/src/tui/input.rs index f2215b2adbc05..f4c853eeae58c 100644 --- a/crates/turborepo-ui/src/tui/input.rs +++ b/crates/turborepo-ui/src/tui/input.rs @@ -76,9 +76,6 @@ fn translate_key_event(options: InputOptions, key_event: KeyEvent) -> Option Some(Event::Input { bytes: encode_key(key_event), }), - // Vim keybinds - KeyCode::Char('j') => Some(Event::Down), - KeyCode::Char('k') => Some(Event::Up), // If we're on the list and user presses `/` enter search mode KeyCode::Char('/') if matches!(options.focus, LayoutSections::TaskList) => { Some(Event::SearchEnter) @@ -115,8 +112,8 @@ fn translate_key_event(options: InputOptions, key_event: KeyEvent) -> Option { Some(Event::ScrollDown) } - KeyCode::Up => Some(Event::Up), - KeyCode::Down => Some(Event::Down), + KeyCode::Up | KeyCode::Char('j') => Some(Event::Up), + KeyCode::Down | KeyCode::Char('k') => Some(Event::Down), KeyCode::Enter | KeyCode::Char('i') => Some(Event::EnterInteractive), _ => None, } From 6d1a8860f6037f5a9a78cb162b1ff64d3d3a98eb Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 25 Nov 2024 09:18:03 -0700 Subject: [PATCH 02/22] wip --- crates/turborepo-lib/src/run/mod.rs | 3 ++- crates/turborepo-ui/src/tui/app.rs | 14 +++++++++++--- crates/turborepo-ui/src/tui/input.rs | 1 + crates/turborepo-ui/src/tui/mod.rs | 1 + crates/turborepo-ui/src/tui/preferences.rs | 10 ++++++++++ 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 crates/turborepo-ui/src/tui/preferences.rs diff --git a/crates/turborepo-lib/src/run/mod.rs b/crates/turborepo-lib/src/run/mod.rs index 448b8c02d4726..47ddd3bf05a3b 100644 --- a/crates/turborepo-lib/src/run/mod.rs +++ b/crates/turborepo-lib/src/run/mod.rs @@ -262,8 +262,9 @@ impl Run { let (sender, receiver) = TuiSender::new(); let color_config = self.color_config; + let repo_root = self.repo_root.clone(); let handle = tokio::task::spawn(async move { - Ok(tui::run_app(task_names, receiver, color_config).await?) + Ok(tui::run_app(task_names, receiver, color_config, repo_root).await?) }); Ok(Some((sender, handle))) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 4fe0faaf06cc8..d149d6bd75056 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -16,6 +16,9 @@ use tokio::{ time::Instant, }; use tracing::{debug, trace}; +use turbopath::AbsoluteSystemPathBuf; + +use crate::tui::preferences; pub const FRAMERATE: Duration = Duration::from_millis(3); const RESIZE_DEBOUNCE_DELAY: Duration = Duration::from_millis(10); @@ -570,6 +573,7 @@ pub async fn run_app( tasks: Vec, receiver: AppReceiver, color_config: ColorConfig, + repo_root: AbsoluteSystemPathBuf, ) -> Result<(), Error> { let mut terminal = startup(color_config)?; let size = terminal.size()?; @@ -579,7 +583,7 @@ pub async fn run_app( input::start_crossterm_stream(crossterm_tx); let (result, callback) = - match run_app_inner(&mut terminal, &mut app, receiver, crossterm_rx).await { + match run_app_inner(&mut terminal, &mut app, receiver, crossterm_rx, repo_root).await { Ok(callback) => (Ok(()), callback), Err(err) => { debug!("tui shutting down: {err}"); @@ -599,6 +603,7 @@ async fn run_app_inner( app: &mut App>, mut receiver: AppReceiver, mut crossterm_rx: mpsc::Receiver, + repo_root: AbsoluteSystemPathBuf, ) -> Result>, Error> { // Render initial state to paint the screen terminal.draw(|f| view(app, f))?; @@ -612,6 +617,7 @@ async fn run_app_inner( if !matches!(event, Event::Tick) { needs_rerender = true; } + let mut event = Some(event); let mut resize_event = None; if matches!(event, Some(Event::Resize { .. })) { @@ -624,10 +630,10 @@ async fn run_app_inner( if let Some(resize) = resize_event.take().or_else(|| resize_debouncer.query()) { // If we got a resize event, make sure to update ratatui backend. terminal.autoresize()?; - update(app, resize)?; + update(app, resize, &repo_root)?; } if let Some(event) = event { - callback = update(app, event)?; + callback = update(app, event, &repo_root)?; if app.done { break; } @@ -735,6 +741,7 @@ fn cleanup( fn update( app: &mut App>, event: Event, + repo_root: &AbsoluteSystemPathBuf, ) -> Result>, Error> { match event { Event::StartTask { task, output_logs } => { @@ -766,6 +773,7 @@ fn update( app.finish_task(&task, result)?; } Event::Up => { + preferences::Preferences::write_preferences(repo_root); app.previous(); } Event::Down => { diff --git a/crates/turborepo-ui/src/tui/input.rs b/crates/turborepo-ui/src/tui/input.rs index f4c853eeae58c..93301c859b08b 100644 --- a/crates/turborepo-ui/src/tui/input.rs +++ b/crates/turborepo-ui/src/tui/input.rs @@ -2,6 +2,7 @@ use crossterm::event::{EventStream, KeyCode, KeyEvent, KeyEventKind, KeyModifier use futures::StreamExt; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::debug; +use turbopath::AbsoluteSystemPathBuf; use super::{ app::LayoutSections, diff --git a/crates/turborepo-ui/src/tui/mod.rs b/crates/turborepo-ui/src/tui/mod.rs index 5e0995829d507..d0707a2b5e825 100644 --- a/crates/turborepo-ui/src/tui/mod.rs +++ b/crates/turborepo-ui/src/tui/mod.rs @@ -5,6 +5,7 @@ pub mod event; mod handle; mod input; mod pane; +mod preferences; mod search; mod size; mod spinner; diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs new file mode 100644 index 0000000000000..a75faeb2c0727 --- /dev/null +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -0,0 +1,10 @@ +use turbopath::AbsoluteSystemPath; + +pub struct Preferences {} + +impl Preferences { + pub fn write_preferences(repo_root: &AbsoluteSystemPath) { + let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); + println!("TODO: save preferences to {:?}", preferences_file); + } +} From ba8cc9ae06ef62cabca7ad2e7ba82439d5a1c009 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Tue, 26 Nov 2024 22:29:49 -0700 Subject: [PATCH 03/22] WIP --- crates/turborepo-ui/src/tui/preferences.rs | 72 ++++++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index a75faeb2c0727..c34cac1e195f2 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -1,10 +1,72 @@ -use turbopath::AbsoluteSystemPath; +use std::{ + fs::{self, File}, + io::{BufReader, Write}, +}; -pub struct Preferences {} +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Preferences { + pub is_task_list_visible: bool, +} + +fn save_to_json( + preferences: &Preferences, + path: AbsoluteSystemPathBuf, +) -> Result<(), Box> { + let json = serde_json::to_string_pretty(preferences)?; + let mut file = File::create(path.as_std_path())?; + file.write_all(json.as_bytes())?; + + Ok(()) +} + +fn update_json_field( + file_path: &str, + field: &str, + new_value: Value, +) -> Result<(), Box> { + let json_string = fs::read_to_string(file_path)?; + let mut json: Value = serde_json::from_str(&json_string)?; + + json[field] = new_value; + let updated_json_string = serde_json::to_string_pretty(&json)?; + + let mut file = fs::File::create(file_path)?; + file.write_all(updated_json_string.as_bytes())?; + + Ok(()) +} + +fn read_from_json(path: &str) -> Result> { + let file = File::open(path)?; + let reader = BufReader::new(file); + + let person: Preferences = serde_json::from_reader(reader)?; + + Ok(person) +} impl Preferences { - pub fn write_preferences(repo_root: &AbsoluteSystemPath) { - let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); - println!("TODO: save preferences to {:?}", preferences_file); + pub fn write_preferences( + repo_root: &AbsoluteSystemPath, + ) -> Result<(), Box> { + let preferences_dir = repo_root.join_components(&[".turbo", "preferences"]); + let preferences_file = preferences_dir.join_component("tui.json"); + + // Create the directory structure if it doesn't exist + fs::create_dir_all(preferences_dir.as_std_path())?; + + save_to_json( + &Preferences { + is_task_list_visible: true, + }, + preferences_file, + ) + .unwrap(); + + Ok(()) } } From a6c1ae14aecd7347069776ba700a369c8fdccb6f Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Wed, 27 Nov 2024 10:43:01 -0700 Subject: [PATCH 04/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 23 +++++- crates/turborepo-ui/src/tui/preferences.rs | 89 ++++++++++++---------- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index d149d6bd75056..1d29e626b762a 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -26,6 +26,7 @@ const RESIZE_DEBOUNCE_DELAY: Duration = Duration::from_millis(10); use super::{ event::{CacheResult, Direction, OutputLogs, PaneSize, TaskResult}, input, + preferences::Preferences, search::SearchResults, AppReceiver, Debouncer, Error, Event, InputOptions, SizeInfo, TaskTable, TerminalPane, }; @@ -773,10 +774,23 @@ fn update( app.finish_task(&task, result)?; } Event::Up => { - preferences::Preferences::write_preferences(repo_root); + preferences::Preferences::write_preferences( + &Preferences { + is_task_list_visible: app.has_sidebar, + active_task: app.active_task()?.to_string(), + }, + repo_root, + ); app.previous(); } Event::Down => { + preferences::Preferences::write_preferences( + &Preferences { + is_task_list_visible: app.has_sidebar, + active_task: app.active_task()?.to_string(), + }, + repo_root, + ); app.next(); } Event::ScrollUp => { @@ -796,6 +810,13 @@ fn update( app.interact()?; } Event::ToggleSidebar => { + preferences::Preferences::write_preferences( + &Preferences { + is_task_list_visible: app.has_sidebar, + active_task: app.active_task()?.to_string(), + }, + repo_root, + ); app.has_sidebar = !app.has_sidebar; } Event::Input { bytes } => { diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index c34cac1e195f2..addec030e49ab 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -5,68 +5,77 @@ use std::{ use serde::{Deserialize, Serialize}; use serde_json::Value; -use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf}; +use turbopath::AbsoluteSystemPathBuf; #[derive(Serialize, Deserialize, Debug)] pub struct Preferences { pub is_task_list_visible: bool, -} - -fn save_to_json( - preferences: &Preferences, - path: AbsoluteSystemPathBuf, -) -> Result<(), Box> { - let json = serde_json::to_string_pretty(preferences)?; - let mut file = File::create(path.as_std_path())?; - file.write_all(json.as_bytes())?; - - Ok(()) -} - -fn update_json_field( - file_path: &str, - field: &str, - new_value: Value, -) -> Result<(), Box> { - let json_string = fs::read_to_string(file_path)?; - let mut json: Value = serde_json::from_str(&json_string)?; - - json[field] = new_value; - let updated_json_string = serde_json::to_string_pretty(&json)?; - - let mut file = fs::File::create(file_path)?; - file.write_all(updated_json_string.as_bytes())?; - - Ok(()) + pub active_task: String, } fn read_from_json(path: &str) -> Result> { let file = File::open(path)?; let reader = BufReader::new(file); - let person: Preferences = serde_json::from_reader(reader)?; + let preferences: Preferences = serde_json::from_reader(reader)?; - Ok(person) + Ok(preferences) +} + +impl Default for Preferences { + fn default() -> Self { + Preferences { + is_task_list_visible: true, + active_task: String::new(), + } + } } impl Preferences { + // pub fn new(is_task_list_visible: bool, active_task: String) -> Self { + // Preferences { + // is_task_list_visible, + // active_task, + // } + // } + pub fn write_preferences( - repo_root: &AbsoluteSystemPath, + &self, + repo_root: &AbsoluteSystemPathBuf, ) -> Result<(), Box> { let preferences_dir = repo_root.join_components(&[".turbo", "preferences"]); let preferences_file = preferences_dir.join_component("tui.json"); - // Create the directory structure if it doesn't exist fs::create_dir_all(preferences_dir.as_std_path())?; - save_to_json( - &Preferences { - is_task_list_visible: true, - }, - preferences_file, - ) - .unwrap(); + let json = serde_json::to_string_pretty(self)?; + let mut file = File::create(preferences_file.as_std_path())?; + file.write_all(json.as_bytes())?; Ok(()) } + + // pub fn read_preferences( + // repo_root: &AbsoluteSystemPathBuf, + // ) -> Result> { + // let preferences_file = repo_root.join_components(&[".turbo", + // "preferences", "tui.json"]); read_from_json(preferences_file. + // as_std_path().to_str().unwrap()) } +} + +fn update_json_field( + file_path: &str, + field: &str, + new_value: Value, +) -> Result<(), Box> { + let json_string = fs::read_to_string(file_path)?; + let mut json: Value = serde_json::from_str(&json_string)?; + + json[field] = new_value; + let updated_json_string = serde_json::to_string_pretty(&json)?; + + let mut file = fs::File::create(file_path)?; + file.write_all(updated_json_string.as_bytes())?; + + Ok(()) } From 7fdd3c5a207d1c80f53c3d50aaa1dbfdbcffb444 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Wed, 27 Nov 2024 22:14:37 -0700 Subject: [PATCH 05/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 69 ++++++++++++++++++---- crates/turborepo-ui/src/tui/preferences.rs | 12 ++-- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 1d29e626b762a..3012561401905 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -61,7 +61,12 @@ pub struct App { } impl App { - pub fn new(rows: u16, cols: u16, tasks: Vec) -> Self { + pub fn new( + rows: u16, + cols: u16, + tasks: Vec, + repo_root: &AbsoluteSystemPathBuf, + ) -> Self { debug!("tasks: {tasks:?}"); let size = SizeInfo::new(rows, cols, tasks.iter().map(|s| s.as_str())); @@ -100,7 +105,9 @@ impl App { tasks_by_status, scroll: TableState::default().with_selected(selected_task_index), selected_task_index, - has_sidebar: true, + has_sidebar: preferences::Preferences::read_preferences(repo_root) + .map(|prefs| prefs.is_task_list_visible) + .unwrap_or(false), has_user_scrolled: has_user_interacted, } } @@ -579,7 +586,8 @@ pub async fn run_app( let mut terminal = startup(color_config)?; let size = terminal.size()?; - let mut app: App> = App::new(size.height, size.width, tasks); + let mut app: App> = + App::new(size.height, size.width, tasks, &repo_root); let (crossterm_tx, crossterm_rx) = mpsc::channel(1024); input::start_crossterm_stream(crossterm_tx); @@ -774,24 +782,24 @@ fn update( app.finish_task(&task, result)?; } Event::Up => { - preferences::Preferences::write_preferences( + app.previous(); + let _ = preferences::Preferences::write_preferences( &Preferences { is_task_list_visible: app.has_sidebar, active_task: app.active_task()?.to_string(), }, repo_root, ); - app.previous(); } Event::Down => { - preferences::Preferences::write_preferences( + app.next(); + let _ = preferences::Preferences::write_preferences( &Preferences { is_task_list_visible: app.has_sidebar, active_task: app.active_task()?.to_string(), }, repo_root, ); - app.next(); } Event::ScrollUp => { app.has_user_scrolled = true; @@ -810,14 +818,14 @@ fn update( app.interact()?; } Event::ToggleSidebar => { - preferences::Preferences::write_preferences( + app.has_sidebar = !app.has_sidebar; + let _ = preferences::Preferences::write_preferences( &Preferences { is_task_list_visible: app.has_sidebar, active_task: app.active_task()?.to_string(), }, repo_root, ); - app.has_sidebar = !app.has_sidebar; } Event::Input { bytes } => { app.forward_input(&bytes)?; @@ -900,6 +908,7 @@ mod test { 100, 100, vec!["foo".to_string(), "bar".to_string(), "baz".to_string()], + &AbsoluteSystemPathBuf::default(), ); assert_eq!( app.scroll.selected(), @@ -929,6 +938,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); assert_eq!(app.scroll.selected(), Some(1), "selected b"); @@ -951,6 +961,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); app.next(); @@ -1017,6 +1028,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); app.next(); @@ -1051,7 +1063,12 @@ mod test { #[test] fn test_forward_stdin() -> Result<(), Error> { - let mut app: App> = App::new(100, 100, vec!["a".to_string(), "b".to_string()]); + let mut app: App> = App::new( + 100, + 100, + vec!["a".to_string(), "b".to_string()], + &AbsoluteSystemPathBuf::default(), + ); app.next(); assert_eq!(app.scroll.selected(), Some(1), "selected b"); assert_eq!(app.tasks_by_status.task_name(1)?, "b", "selected b"); @@ -1084,7 +1101,12 @@ mod test { #[test] fn test_interact() -> Result<(), Error> { - let mut app: App> = App::new(100, 100, vec!["a".to_string(), "b".to_string()]); + let mut app: App> = App::new( + 100, + 100, + vec!["a".to_string(), "b".to_string()], + &AbsoluteSystemPathBuf::default(), + ); assert!(!app.is_focusing_pane(), "app starts focused on table"); app.insert_stdin("a", Some(Vec::new()))?; @@ -1106,7 +1128,12 @@ mod test { #[test] fn test_task_status() -> Result<(), Error> { - let mut app: App> = App::new(100, 100, vec!["a".to_string(), "b".to_string()]); + let mut app: App> = App::new( + 100, + 100, + vec!["a".to_string(), "b".to_string()], + &AbsoluteSystemPathBuf::default(), + ); app.next(); assert_eq!(app.scroll.selected(), Some(1), "selected b"); assert_eq!(app.tasks_by_status.task_name(1)?, "b", "selected b"); @@ -1127,6 +1154,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); assert_eq!(app.scroll.selected(), Some(0), "selected a"); assert_eq!(app.tasks_by_status.task_name(0)?, "a", "selected a"); @@ -1157,6 +1185,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); assert_eq!(app.scroll.selected(), Some(1), "selected b"); @@ -1184,7 +1213,12 @@ mod test { #[test] fn test_resize() -> Result<(), Error> { - let mut app: App> = App::new(20, 24, vec!["a".to_string(), "b".to_string()]); + let mut app: App> = App::new( + 20, + 24, + vec!["a".to_string(), "b".to_string()], + &AbsoluteSystemPathBuf::default(), + ); let pane_rows = app.size.pane_rows(); let pane_cols = app.size.pane_cols(); for (name, task) in app.tasks.iter() { @@ -1219,6 +1253,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); app.update_tasks(Vec::new())?; @@ -1233,6 +1268,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); app.restart_tasks(vec!["d".to_string()])?; @@ -1249,6 +1285,7 @@ mod test { 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.enter_search()?; assert!(matches!(app.focus, LayoutSections::Search { .. })); @@ -1270,6 +1307,7 @@ mod test { 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.enter_search()?; app.search_enter_char('a')?; @@ -1293,6 +1331,7 @@ mod test { 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.enter_search()?; app.search_enter_char('b')?; @@ -1320,6 +1359,7 @@ mod test { 100, 100, vec!["a".to_string(), "abc".to_string(), "b".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); assert_eq!(app.active_task()?, "abc"); @@ -1339,6 +1379,7 @@ mod test { 100, 100, vec!["a".to_string(), "abc".to_string(), "b".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.next(); assert_eq!(app.active_task()?, "abc"); @@ -1358,6 +1399,7 @@ mod test { 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.enter_search()?; app.search_enter_char('b')?; @@ -1374,6 +1416,7 @@ mod test { 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], + &AbsoluteSystemPathBuf::default(), ); app.enter_search()?; app.search_enter_char('b')?; diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index addec030e49ab..3f23ff2a31ca4 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -55,12 +55,12 @@ impl Preferences { Ok(()) } - // pub fn read_preferences( - // repo_root: &AbsoluteSystemPathBuf, - // ) -> Result> { - // let preferences_file = repo_root.join_components(&[".turbo", - // "preferences", "tui.json"]); read_from_json(preferences_file. - // as_std_path().to_str().unwrap()) } + pub fn read_preferences( + repo_root: &AbsoluteSystemPathBuf, + ) -> Result> { + let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); + read_from_json(preferences_file.as_std_path().to_str().unwrap()) + } } fn update_json_field( From 83cee6b97da93e4d9c0fa17466cf0ef15730c440 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Thu, 28 Nov 2024 19:58:41 -0700 Subject: [PATCH 06/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 29 +++++++++++++++++++--- crates/turborepo-ui/src/tui/preferences.rs | 7 ------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 3012561401905..e6769cea13c18 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -5,6 +5,7 @@ use std::{ time::Duration, }; +use itertools::Itertools; use ratatui::{ backend::{Backend, CrosstermBackend}, layout::{Constraint, Layout}, @@ -83,8 +84,28 @@ impl App { running: Vec::new(), }; - let has_user_interacted = false; - let selected_task_index: usize = 0; + let preferences_from_disk = preferences::Preferences::read_preferences(repo_root); + let task_name_selected_from_previous_invocation = preferences_from_disk + .as_ref() + .map(|prefs| prefs.active_task.clone()) + .ok(); + + let has_selected_task_from_previous_invocation = + task_name_selected_from_previous_invocation.is_some(); + + let task_selected_from_previous_invocation = preferences_from_disk + .as_ref() + .map(|prefs| { + tasks_by_status + .task_names_in_displayed_order() + .find_position(|task_name| *task_name == prefs.active_task) + .unwrap() + .0 + }) + .unwrap_or(0); + + let has_user_interacted = has_selected_task_from_previous_invocation; + let selected_task_index: usize = task_selected_from_previous_invocation; let pane_rows = size.pane_rows(); let pane_cols = size.pane_cols(); @@ -105,9 +126,9 @@ impl App { tasks_by_status, scroll: TableState::default().with_selected(selected_task_index), selected_task_index, - has_sidebar: preferences::Preferences::read_preferences(repo_root) + has_sidebar: preferences_from_disk .map(|prefs| prefs.is_task_list_visible) - .unwrap_or(false), + .unwrap_or(true), has_user_scrolled: has_user_interacted, } } diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 3f23ff2a31ca4..e622d88973936 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -32,13 +32,6 @@ impl Default for Preferences { } impl Preferences { - // pub fn new(is_task_list_visible: bool, active_task: String) -> Self { - // Preferences { - // is_task_list_visible, - // active_task, - // } - // } - pub fn write_preferences( &self, repo_root: &AbsoluteSystemPathBuf, From 40e4f189b2f4c12792f3d3d38344884acb0cf008 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Thu, 28 Nov 2024 21:05:02 -0700 Subject: [PATCH 07/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 34 ++++----- crates/turborepo-ui/src/tui/input.rs | 2 +- crates/turborepo-ui/src/tui/preferences.rs | 80 +++++++++++----------- 3 files changed, 56 insertions(+), 60 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index e6769cea13c18..3b825ce78f007 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -27,7 +27,6 @@ const RESIZE_DEBOUNCE_DELAY: Duration = Duration::from_millis(10); use super::{ event::{CacheResult, Direction, OutputLogs, PaneSize, TaskResult}, input, - preferences::Preferences, search::SearchResults, AppReceiver, Debouncer, Error, Event, InputOptions, SizeInfo, TaskTable, TerminalPane, }; @@ -85,10 +84,11 @@ impl App { }; let preferences_from_disk = preferences::Preferences::read_preferences(repo_root); + eprintln!("{:?}", preferences_from_disk); let task_name_selected_from_previous_invocation = preferences_from_disk .as_ref() .map(|prefs| prefs.active_task.clone()) - .ok(); + .unwrap(); let has_selected_task_from_previous_invocation = task_name_selected_from_previous_invocation.is_some(); @@ -98,7 +98,8 @@ impl App { .map(|prefs| { tasks_by_status .task_names_in_displayed_order() - .find_position(|task_name| *task_name == prefs.active_task) + .find_position(|task_name| Some(task_name.to_string()) == prefs.active_task) + // TODO: This is causing a crash. .unwrap() .0 }) @@ -128,7 +129,8 @@ impl App { selected_task_index, has_sidebar: preferences_from_disk .map(|prefs| prefs.is_task_list_visible) - .unwrap_or(true), + .unwrap_or(Some(true)) + .unwrap(), has_user_scrolled: has_user_interacted, } } @@ -804,22 +806,18 @@ fn update( } Event::Up => { app.previous(); - let _ = preferences::Preferences::write_preferences( - &Preferences { - is_task_list_visible: app.has_sidebar, - active_task: app.active_task()?.to_string(), - }, + let _ = preferences::Preferences::update_preference( repo_root, + preferences::PreferenceFields::ActiveTask, + serde_json::Value::String(app.active_task()?.to_string()), ); } Event::Down => { app.next(); - let _ = preferences::Preferences::write_preferences( - &Preferences { - is_task_list_visible: app.has_sidebar, - active_task: app.active_task()?.to_string(), - }, + let _ = preferences::Preferences::update_preference( repo_root, + preferences::PreferenceFields::ActiveTask, + serde_json::Value::String(app.active_task()?.to_string()), ); } Event::ScrollUp => { @@ -840,12 +838,10 @@ fn update( } Event::ToggleSidebar => { app.has_sidebar = !app.has_sidebar; - let _ = preferences::Preferences::write_preferences( - &Preferences { - is_task_list_visible: app.has_sidebar, - active_task: app.active_task()?.to_string(), - }, + let _ = preferences::Preferences::update_preference( repo_root, + preferences::PreferenceFields::IsTaskListVisible, + serde_json::Value::Bool(app.has_sidebar), ); } Event::Input { bytes } => { diff --git a/crates/turborepo-ui/src/tui/input.rs b/crates/turborepo-ui/src/tui/input.rs index 66a58b2deca12..d4d2c89980f36 100644 --- a/crates/turborepo-ui/src/tui/input.rs +++ b/crates/turborepo-ui/src/tui/input.rs @@ -86,7 +86,6 @@ fn translate_key_event(options: InputOptions, key_event: KeyEvent) -> Option Some(Event::ToggleSidebar), KeyCode::Enter if matches!(options.focus, LayoutSections::Search { .. }) => { Some(Event::SearchExit { restore_scroll: false, @@ -109,6 +108,7 @@ fn translate_key_event(options: InputOptions, key_event: KeyEvent) -> Option Some(Event::ToggleSidebar), KeyCode::Char('p') if key_event.modifiers == KeyModifiers::CONTROL => Some(Event::ScrollUp), KeyCode::Char('n') if key_event.modifiers == KeyModifiers::CONTROL => { Some(Event::ScrollDown) diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index e622d88973936..1edf68d5e26ef 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -4,46 +4,53 @@ use std::{ }; use serde::{Deserialize, Serialize}; -use serde_json::Value; +use serde_json::{json, Value}; use turbopath::AbsoluteSystemPathBuf; #[derive(Serialize, Deserialize, Debug)] pub struct Preferences { - pub is_task_list_visible: bool, - pub active_task: String, + pub is_task_list_visible: Option, + pub active_task: Option, } -fn read_from_json(path: &str) -> Result> { - let file = File::open(path)?; - let reader = BufReader::new(file); - - let preferences: Preferences = serde_json::from_reader(reader)?; - - Ok(preferences) -} - -impl Default for Preferences { - fn default() -> Self { - Preferences { - is_task_list_visible: true, - active_task: String::new(), - } - } +#[derive(Debug)] +pub enum PreferenceFields { + IsTaskListVisible, + ActiveTask, } impl Preferences { - pub fn write_preferences( - &self, + pub fn update_preference( repo_root: &AbsoluteSystemPathBuf, + field: PreferenceFields, + new_value: Value, ) -> Result<(), Box> { let preferences_dir = repo_root.join_components(&[".turbo", "preferences"]); - let preferences_file = preferences_dir.join_component("tui.json"); + let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); fs::create_dir_all(preferences_dir.as_std_path())?; - let json = serde_json::to_string_pretty(self)?; - let mut file = File::create(preferences_file.as_std_path())?; - file.write_all(json.as_bytes())?; + let mut json: Value = if preferences_file.exists() { + let json_string = fs::read_to_string(&preferences_file)?; + serde_json::from_str(&json_string)? + } else { + json!({}) + }; + + // TODO: Is this really how to do this? No way, right? + match field { + PreferenceFields::IsTaskListVisible => { + json["is_task_list_visible"] = new_value; + } + PreferenceFields::ActiveTask => { + json["active_task"] = new_value; + } + } + + let updated_json_string = serde_json::to_string_pretty(&json)?; + + let mut file = fs::File::create(&preferences_file)?; + file.write_all(updated_json_string.as_bytes())?; Ok(()) } @@ -52,23 +59,16 @@ impl Preferences { repo_root: &AbsoluteSystemPathBuf, ) -> Result> { let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); - read_from_json(preferences_file.as_std_path().to_str().unwrap()) - } -} -fn update_json_field( - file_path: &str, - field: &str, - new_value: Value, -) -> Result<(), Box> { - let json_string = fs::read_to_string(file_path)?; - let mut json: Value = serde_json::from_str(&json_string)?; + fn read_from_json(path: &str) -> Result> { + let file = File::open(path)?; + let reader = BufReader::new(file); - json[field] = new_value; - let updated_json_string = serde_json::to_string_pretty(&json)?; + let preferences: Preferences = serde_json::from_reader(reader)?; - let mut file = fs::File::create(file_path)?; - file.write_all(updated_json_string.as_bytes())?; + Ok(preferences) + } - Ok(()) + read_from_json(preferences_file.as_std_path().to_str().unwrap()) + } } From 1eb597e6f1581c5cfdfaf59367a2a7ae0cb88de6 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Fri, 29 Nov 2024 09:40:53 -0700 Subject: [PATCH 08/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 42 +++++++++++++++++++--- crates/turborepo-ui/src/tui/event.rs | 1 + crates/turborepo-ui/src/tui/input.rs | 1 + crates/turborepo-ui/src/tui/preferences.rs | 5 +++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 3b825ce78f007..d3c244c351bb2 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -90,8 +90,12 @@ impl App { .map(|prefs| prefs.active_task.clone()) .unwrap(); - let has_selected_task_from_previous_invocation = - task_name_selected_from_previous_invocation.is_some(); + let has_selected_task_from_previous_invocation = task_name_selected_from_previous_invocation + .is_some() + && preferences_from_disk + .as_ref() + .map(|prefs| prefs.is_pinned_task_selection.unwrap_or(false)) + .unwrap_or(false); let task_selected_from_previous_invocation = preferences_from_disk .as_ref() @@ -99,8 +103,7 @@ impl App { tasks_by_status .task_names_in_displayed_order() .find_position(|task_name| Some(task_name.to_string()) == prefs.active_task) - // TODO: This is causing a crash. - .unwrap() + .unwrap_or((0, "")) .0 }) .unwrap_or(0); @@ -811,6 +814,12 @@ fn update( preferences::PreferenceFields::ActiveTask, serde_json::Value::String(app.active_task()?.to_string()), ); + + let _ = preferences::Preferences::update_preference( + repo_root, + preferences::PreferenceFields::PinnedTaskSelection, + serde_json::Value::Bool(true), + ); } Event::Down => { app.next(); @@ -819,6 +828,12 @@ fn update( preferences::PreferenceFields::ActiveTask, serde_json::Value::String(app.active_task()?.to_string()), ); + + let _ = preferences::Preferences::update_preference( + repo_root, + preferences::PreferenceFields::PinnedTaskSelection, + serde_json::Value::Bool(true), + ); } Event::ScrollUp => { app.has_user_scrolled = true; @@ -836,6 +851,25 @@ fn update( app.has_user_scrolled = true; app.interact()?; } + Event::TogglePinnedTask => { + app.has_user_scrolled = !app.has_user_scrolled; + let _ = preferences::Preferences::update_preference( + repo_root, + preferences::PreferenceFields::PinnedTaskSelection, + serde_json::Value::Bool(app.has_user_scrolled), + ); + + // Clear the selected task because the user has expressed that they don't care + // which task they're looking at right now. If they do care, they will either + // pin again or scroll through the task list. + if !app.has_user_scrolled { + let _ = preferences::Preferences::update_preference( + repo_root, + preferences::PreferenceFields::ActiveTask, + serde_json::Value::String("".to_string()), + ); + } + } Event::ToggleSidebar => { app.has_sidebar = !app.has_sidebar; let _ = preferences::Preferences::update_preference( diff --git a/crates/turborepo-ui/src/tui/event.rs b/crates/turborepo-ui/src/tui/event.rs index 446f110d8f176..ea9b83232c55d 100644 --- a/crates/turborepo-ui/src/tui/event.rs +++ b/crates/turborepo-ui/src/tui/event.rs @@ -50,6 +50,7 @@ pub enum Event { rows: u16, cols: u16, }, + TogglePinnedTask, ToggleSidebar, SearchEnter, SearchExit { diff --git a/crates/turborepo-ui/src/tui/input.rs b/crates/turborepo-ui/src/tui/input.rs index d4d2c89980f36..ccb53e8c5cec4 100644 --- a/crates/turborepo-ui/src/tui/input.rs +++ b/crates/turborepo-ui/src/tui/input.rs @@ -110,6 +110,7 @@ fn translate_key_event(options: InputOptions, key_event: KeyEvent) -> Option Some(Event::ToggleSidebar), KeyCode::Char('p') if key_event.modifiers == KeyModifiers::CONTROL => Some(Event::ScrollUp), + KeyCode::Char('p') => Some(Event::TogglePinnedTask), KeyCode::Char('n') if key_event.modifiers == KeyModifiers::CONTROL => { Some(Event::ScrollDown) } diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 1edf68d5e26ef..adc13b13c5970 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -11,12 +11,14 @@ use turbopath::AbsoluteSystemPathBuf; pub struct Preferences { pub is_task_list_visible: Option, pub active_task: Option, + pub is_pinned_task_selection: Option, } #[derive(Debug)] pub enum PreferenceFields { IsTaskListVisible, ActiveTask, + PinnedTaskSelection, } impl Preferences { @@ -45,6 +47,9 @@ impl Preferences { PreferenceFields::ActiveTask => { json["active_task"] = new_value; } + PreferenceFields::PinnedTaskSelection => { + json["is_pinned_task_selection"] = new_value; + } } let updated_json_string = serde_json::to_string_pretty(&json)?; From b6853466f99142c92eaab134b7cdeb30fa90efa0 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Fri, 29 Nov 2024 11:33:55 -0700 Subject: [PATCH 09/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 45 +++---------- crates/turborepo-ui/src/tui/preferences.rs | 74 ++++++++++++++++++---- 2 files changed, 71 insertions(+), 48 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index d3c244c351bb2..6e3a3cdea458e 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -5,7 +5,6 @@ use std::{ time::Duration, }; -use itertools::Itertools; use ratatui::{ backend::{Backend, CrosstermBackend}, layout::{Constraint, Layout}, @@ -83,34 +82,6 @@ impl App { running: Vec::new(), }; - let preferences_from_disk = preferences::Preferences::read_preferences(repo_root); - eprintln!("{:?}", preferences_from_disk); - let task_name_selected_from_previous_invocation = preferences_from_disk - .as_ref() - .map(|prefs| prefs.active_task.clone()) - .unwrap(); - - let has_selected_task_from_previous_invocation = task_name_selected_from_previous_invocation - .is_some() - && preferences_from_disk - .as_ref() - .map(|prefs| prefs.is_pinned_task_selection.unwrap_or(false)) - .unwrap_or(false); - - let task_selected_from_previous_invocation = preferences_from_disk - .as_ref() - .map(|prefs| { - tasks_by_status - .task_names_in_displayed_order() - .find_position(|task_name| Some(task_name.to_string()) == prefs.active_task) - .unwrap_or((0, "")) - .0 - }) - .unwrap_or(0); - - let has_user_interacted = has_selected_task_from_previous_invocation; - let selected_task_index: usize = task_selected_from_previous_invocation; - let pane_rows = size.pane_rows(); let pane_cols = size.pane_cols(); @@ -127,14 +98,16 @@ impl App { ) }) .collect(), + scroll: TableState::default().with_selected( + preferences::Preferences::get_selected_task_index(repo_root, &tasks_by_status), + ), + selected_task_index: preferences::Preferences::get_selected_task_index( + repo_root, + &tasks_by_status, + ), tasks_by_status, - scroll: TableState::default().with_selected(selected_task_index), - selected_task_index, - has_sidebar: preferences_from_disk - .map(|prefs| prefs.is_task_list_visible) - .unwrap_or(Some(true)) - .unwrap(), - has_user_scrolled: has_user_interacted, + has_sidebar: preferences::Preferences::read_task_list_visibility(repo_root), + has_user_scrolled: preferences::Preferences::read_pinned_task_state(repo_root), } } diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index adc13b13c5970..ed5d5ace9582f 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -4,9 +4,11 @@ use std::{ }; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use serde_json::{from_reader, json, Value}; use turbopath::AbsoluteSystemPathBuf; +use super::task::TasksByStatus; + #[derive(Serialize, Deserialize, Debug)] pub struct Preferences { pub is_task_list_visible: Option, @@ -21,7 +23,32 @@ pub enum PreferenceFields { PinnedTaskSelection, } +impl Default for Preferences { + fn default() -> Self { + Self { + active_task: None, + is_task_list_visible: Some(true), + is_pinned_task_selection: Some(false), + } + } +} + +const TUI_PREFERENCES_PATH_COMPONENTS: &[&str] = &[".turbo", "preferences", "tui.json"]; + +fn read_json(path: &AbsoluteSystemPathBuf) -> Preferences { + File::open(path) + .ok() + .and_then(|file| from_reader(BufReader::new(file)).ok()) + .unwrap_or_else(|| Preferences::default()) +} + impl Preferences { + pub fn get_all_preferences(repo_root: &AbsoluteSystemPathBuf) -> Preferences { + let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); + + read_json(&preferences_file) + } + pub fn update_preference( repo_root: &AbsoluteSystemPathBuf, field: PreferenceFields, @@ -60,20 +87,43 @@ impl Preferences { Ok(()) } - pub fn read_preferences( - repo_root: &AbsoluteSystemPathBuf, - ) -> Result> { - let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); + pub fn read_pinned_task_state(repo_root: &AbsoluteSystemPathBuf) -> bool { + let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); + + read_json(&preferences_file) + .is_pinned_task_selection + .unwrap_or(false) + } - fn read_from_json(path: &str) -> Result> { - let file = File::open(path)?; - let reader = BufReader::new(file); + pub fn read_task_list_visibility(repo_root: &AbsoluteSystemPathBuf) -> bool { + let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); - let preferences: Preferences = serde_json::from_reader(reader)?; + read_json(&preferences_file) + .is_task_list_visible + .unwrap_or(false) + } - Ok(preferences) - } + pub fn read_selected_task(repo_root: &AbsoluteSystemPathBuf) -> String { + let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); + + read_json(&preferences_file) + .active_task + .unwrap_or("".to_string()) + } + + pub fn get_selected_task_index( + repo_root: &AbsoluteSystemPathBuf, + tasks_by_status: &TasksByStatus, + ) -> usize { + let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); + + let selected_task_name = read_json(&preferences_file) + .active_task + .unwrap_or("".to_string()); - read_from_json(preferences_file.as_std_path().to_str().unwrap()) + tasks_by_status + .task_names_in_displayed_order() + .position(|task_name| task_name.to_string() == selected_task_name) + .unwrap_or(0) } } From 78bb0b60b91e2a428c0b2c6ba03a55551b1d5396 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Fri, 29 Nov 2024 12:09:28 -0700 Subject: [PATCH 10/22] WIP --- crates/turborepo-ui/src/tui/preferences.rs | 41 ++++++++++++---------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index ed5d5ace9582f..7b214a8e999d4 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -39,21 +39,16 @@ fn read_json(path: &AbsoluteSystemPathBuf) -> Preferences { File::open(path) .ok() .and_then(|file| from_reader(BufReader::new(file)).ok()) - .unwrap_or_else(|| Preferences::default()) + .unwrap_or_default() } impl Preferences { - pub fn get_all_preferences(repo_root: &AbsoluteSystemPathBuf) -> Preferences { - let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); - - read_json(&preferences_file) - } - pub fn update_preference( repo_root: &AbsoluteSystemPathBuf, field: PreferenceFields, new_value: Value, ) -> Result<(), Box> { + // TODO: Clean these up, should be taken from constants let preferences_dir = repo_root.join_components(&[".turbo", "preferences"]); let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); @@ -100,15 +95,7 @@ impl Preferences { read_json(&preferences_file) .is_task_list_visible - .unwrap_or(false) - } - - pub fn read_selected_task(repo_root: &AbsoluteSystemPathBuf) -> String { - let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); - - read_json(&preferences_file) - .active_task - .unwrap_or("".to_string()) + .unwrap_or(true) } pub fn get_selected_task_index( @@ -121,9 +108,25 @@ impl Preferences { .active_task .unwrap_or("".to_string()); - tasks_by_status + match tasks_by_status .task_names_in_displayed_order() - .position(|task_name| task_name.to_string() == selected_task_name) - .unwrap_or(0) + .position(|task_name| *task_name == selected_task_name) + { + Some(index) => index, + None => { + let _ = Self::update_preference( + repo_root, + PreferenceFields::PinnedTaskSelection, + serde_json::Value::Bool(false), + ); + + let _ = Self::update_preference( + repo_root, + PreferenceFields::ActiveTask, + serde_json::Value::String("".to_string()), + ); + 0 + } + } } } From 0e65fa3f70450915125a2d2fd74dcdb4121f2d33 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Fri, 29 Nov 2024 12:15:21 -0700 Subject: [PATCH 11/22] WIP --- crates/turborepo-ui/src/tui/input.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/turborepo-ui/src/tui/input.rs b/crates/turborepo-ui/src/tui/input.rs index ccb53e8c5cec4..2274c25487353 100644 --- a/crates/turborepo-ui/src/tui/input.rs +++ b/crates/turborepo-ui/src/tui/input.rs @@ -2,7 +2,6 @@ use crossterm::event::{EventStream, KeyCode, KeyEvent, KeyEventKind, KeyModifier use futures::StreamExt; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::debug; -use turbopath::AbsoluteSystemPathBuf; use super::{ app::LayoutSections, From c00b7ed558f403657799588681a7e7a620584afe Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 14:15:30 -0700 Subject: [PATCH 12/22] WIP --- crates/turborepo-lib/src/run/mod.rs | 2 +- crates/turborepo-ui/src/tui/app.rs | 99 +++++++--------------- crates/turborepo-ui/src/tui/mod.rs | 2 + crates/turborepo-ui/src/tui/preferences.rs | 56 +++++++++++- crates/turborepo-ui/src/tui/task.rs | 5 ++ 5 files changed, 92 insertions(+), 72 deletions(-) diff --git a/crates/turborepo-lib/src/run/mod.rs b/crates/turborepo-lib/src/run/mod.rs index 3561b4385f484..7ea0389e711fc 100644 --- a/crates/turborepo-lib/src/run/mod.rs +++ b/crates/turborepo-lib/src/run/mod.rs @@ -264,7 +264,7 @@ impl Run { let color_config = self.color_config; let repo_root = self.repo_root.clone(); let handle = tokio::task::spawn(async move { - Ok(tui::run_app(task_names, receiver, color_config, repo_root).await?) + Ok(tui::run_app(task_names, receiver, color_config, &repo_root).await?) }); Ok(Some((sender, handle))) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index a043c352cdcd8..0145b7ddf047f 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -18,10 +18,7 @@ use tokio::{ use tracing::{debug, trace}; use turbopath::AbsoluteSystemPathBuf; -use crate::tui::{ - popup::{popup, popup_area}, - preferences, -}; +use crate::tui::popup::{popup, popup_area}; pub const FRAMERATE: Duration = Duration::from_millis(3); const RESIZE_DEBOUNCE_DELAY: Duration = Duration::from_millis(10); @@ -29,6 +26,7 @@ const RESIZE_DEBOUNCE_DELAY: Duration = Duration::from_millis(10); use super::{ event::{CacheResult, Direction, OutputLogs, PaneSize, TaskResult}, input, + preferences::PreferenceLoader, search::SearchResults, AppReceiver, Debouncer, Error, Event, InputOptions, SizeInfo, TaskTable, TerminalPane, }; @@ -61,15 +59,11 @@ pub struct App { has_sidebar: bool, showing_help_popup: bool, done: bool, + preferences: PreferenceLoader, } impl App { - pub fn new( - rows: u16, - cols: u16, - tasks: Vec, - repo_root: &AbsoluteSystemPathBuf, - ) -> Self { + pub fn new(rows: u16, cols: u16, tasks: Vec, preferences: PreferenceLoader) -> Self { debug!("tasks: {tasks:?}"); let size = SizeInfo::new(rows, cols, tasks.iter().map(|s| s.as_str())); @@ -89,6 +83,12 @@ impl App { let pane_rows = size.pane_rows(); let pane_cols = size.pane_cols(); + // Attempt to load previous selection. If not, go to 0. + let selected_task_index = preferences + .active_task() + .and_then(|active_task| tasks_by_status.active_index(active_task)) + .unwrap_or(0); + Self { size, done: false, @@ -102,17 +102,13 @@ impl App { ) }) .collect(), - selected_task_index: preferences::Preferences::get_selected_task_index( - repo_root, - &tasks_by_status, - ), + selected_task_index, tasks_by_status: tasks_by_status.clone(), - task_list_scroll: TableState::default().with_selected( - preferences::Preferences::get_selected_task_index(repo_root, &tasks_by_status), - ), - has_sidebar: preferences::Preferences::read_task_list_visibility(repo_root), + task_list_scroll: TableState::default().with_selected(selected_task_index), + has_sidebar: preferences.is_task_list_visible(), showing_help_popup: false, - is_task_selection_pinned: preferences::Preferences::read_pinned_task_state(repo_root), + is_task_selection_pinned: preferences.active_task().is_some(), + preferences, } } @@ -155,6 +151,15 @@ impl App { .ok_or_else(|| Error::TaskNotFound { name: active_task }) } + fn persist_active_task(&mut self) -> Result<(), Error> { + let active_task = self.active_task()?; + self.preferences.set_active_task( + self.is_task_selection_pinned + .then(|| active_task.to_owned()), + ); + Ok(()) + } + #[tracing::instrument(skip(self))] pub fn next(&mut self) { let num_rows = self.tasks_by_status.count_all(); @@ -586,18 +591,19 @@ pub async fn run_app( tasks: Vec, receiver: AppReceiver, color_config: ColorConfig, - repo_root: AbsoluteSystemPathBuf, + repo_root: &AbsoluteSystemPathBuf, ) -> Result<(), Error> { let mut terminal = startup(color_config)?; let size = terminal.size()?; + let preferences = PreferenceLoader::new(repo_root)?; let mut app: App> = - App::new(size.height, size.width, tasks, &repo_root); + App::new(size.height, size.width, tasks, preferences); let (crossterm_tx, crossterm_rx) = mpsc::channel(1024); input::start_crossterm_stream(crossterm_tx); let (result, callback) = - match run_app_inner(&mut terminal, &mut app, receiver, crossterm_rx, repo_root).await { + match run_app_inner(&mut terminal, &mut app, receiver, crossterm_rx).await { Ok(callback) => (Ok(()), callback), Err(err) => { debug!("tui shutting down: {err}"); @@ -617,7 +623,6 @@ async fn run_app_inner( app: &mut App>, mut receiver: AppReceiver, mut crossterm_rx: mpsc::Receiver, - repo_root: AbsoluteSystemPathBuf, ) -> Result>, Error> { // Render initial state to paint the screen terminal.draw(|f| view(app, f))?; @@ -644,10 +649,10 @@ async fn run_app_inner( if let Some(resize) = resize_event.take().or_else(|| resize_debouncer.query()) { // If we got a resize event, make sure to update ratatui backend. terminal.autoresize()?; - update(app, resize, &repo_root)?; + update(app, resize)?; } if let Some(event) = event { - callback = update(app, event, &repo_root)?; + callback = update(app, event)?; if app.done { break; } @@ -755,7 +760,6 @@ fn cleanup( fn update( app: &mut App>, event: Event, - repo_root: &AbsoluteSystemPathBuf, ) -> Result>, Error> { match event { Event::StartTask { task, output_logs } => { @@ -788,31 +792,9 @@ fn update( } Event::Up => { app.previous(); - let _ = preferences::Preferences::update_preference( - repo_root, - preferences::PreferenceFields::ActiveTask, - serde_json::Value::String(app.active_task()?.to_string()), - ); - - let _ = preferences::Preferences::update_preference( - repo_root, - preferences::PreferenceFields::PinnedTaskSelection, - serde_json::Value::Bool(true), - ); } Event::Down => { app.next(); - let _ = preferences::Preferences::update_preference( - repo_root, - preferences::PreferenceFields::ActiveTask, - serde_json::Value::String(app.active_task()?.to_string()), - ); - - let _ = preferences::Preferences::update_preference( - repo_root, - preferences::PreferenceFields::PinnedTaskSelection, - serde_json::Value::Bool(true), - ); } Event::ScrollUp => { app.is_task_selection_pinned = true; @@ -832,30 +814,9 @@ fn update( } Event::TogglePinnedTask => { app.is_task_selection_pinned = !app.is_task_selection_pinned; - let _ = preferences::Preferences::update_preference( - repo_root, - preferences::PreferenceFields::PinnedTaskSelection, - serde_json::Value::Bool(app.is_task_selection_pinned), - ); - - // Clear the selected task because the user has expressed that they don't care - // which task they're looking at right now. If they do care, they will either - // pin again or scroll through the task list. - if !app.is_task_selection_pinned { - let _ = preferences::Preferences::update_preference( - repo_root, - preferences::PreferenceFields::ActiveTask, - serde_json::Value::String("".to_string()), - ); - } } Event::ToggleSidebar => { app.has_sidebar = !app.has_sidebar; - let _ = preferences::Preferences::update_preference( - repo_root, - preferences::PreferenceFields::IsTaskListVisible, - serde_json::Value::Bool(app.has_sidebar), - ); } Event::ToggleHelpPopup => { app.showing_help_popup = !app.showing_help_popup; diff --git a/crates/turborepo-ui/src/tui/mod.rs b/crates/turborepo-ui/src/tui/mod.rs index 8edfe9f1bb1f3..274c6edfadc3c 100644 --- a/crates/turborepo-ui/src/tui/mod.rs +++ b/crates/turborepo-ui/src/tui/mod.rs @@ -37,4 +37,6 @@ pub enum Error { Stdin { name: String, e: std::io::Error }, #[error(transparent)] Io(#[from] std::io::Error), + #[error("Unable to persist preferences")] + Preferences(#[from] preferences::Error), } diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 7b214a8e999d4..e58c7943e68d7 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -9,6 +9,60 @@ use turbopath::AbsoluteSystemPathBuf; use super::task::TasksByStatus; +const TUI_PREFERENCES_PATH_COMPONENTS: &[&str] = &[".turbo", "preferences", "tui.json"]; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Serde(#[from] serde_json::Error), +} + +pub struct PreferenceLoader { + file_path: AbsoluteSystemPathBuf, + config: Preferences, +} + +impl PreferenceLoader { + pub fn new(repo_root: &AbsoluteSystemPathBuf) -> Result { + let file_path = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); + let contents = file_path.read_existing_to_string()?; + let config = contents + .map(|string| serde_json::from_str(&string)) + .transpose()? + .unwrap_or_default(); + + Ok(Self { file_path, config }) + } + + pub fn is_task_list_visible(&self) -> bool { + self.config.is_task_list_visible.unwrap_or(true) + } + + pub fn active_task(&self) -> Option<&str> { + let active_task = self.config.active_task.as_deref()?; + Some(active_task) + } + + pub fn is_pinned_task_selection(&self) -> bool { + self.config.is_pinned_task_selection.unwrap_or(true) + } + + pub fn set_active_task(&mut self, value: Option) -> Result<(), Error> { + self.config.active_task = value; + self.flush_to_disk() + } + + fn flush_to_disk(&self) -> Result<(), Error> { + self.file_path.ensure_dir(); + self.file_path + .create_with_contents(serde_json::to_string_pretty(&self.config)?)?; + + Ok(()) + } +} + #[derive(Serialize, Deserialize, Debug)] pub struct Preferences { pub is_task_list_visible: Option, @@ -33,8 +87,6 @@ impl Default for Preferences { } } -const TUI_PREFERENCES_PATH_COMPONENTS: &[&str] = &[".turbo", "preferences", "tui.json"]; - fn read_json(path: &AbsoluteSystemPathBuf) -> Preferences { File::open(path) .ok() diff --git a/crates/turborepo-ui/src/tui/task.rs b/crates/turborepo-ui/src/tui/task.rs index 55650f41cfd84..f72c87ee5b5db 100644 --- a/crates/turborepo-ui/src/tui/task.rs +++ b/crates/turborepo-ui/src/tui/task.rs @@ -138,6 +138,11 @@ impl TasksByStatus { running_names.chain(planned_names).chain(finished_names) } + pub fn active_index(&self, task_name: &str) -> Option { + self.task_names_in_displayed_order() + .position(|task| task == task_name) + } + pub fn task_name(&self, index: usize) -> Result<&str, Error> { self.task_names_in_displayed_order() .nth(index) From d0f01866a10d84628fa8946d7c8471dcfd93307c Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 15:13:41 -0700 Subject: [PATCH 13/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 14 ++++++++++---- crates/turborepo-ui/src/tui/preferences.rs | 5 +++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 0145b7ddf047f..03accdc8292c3 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -156,7 +156,7 @@ impl App { self.preferences.set_active_task( self.is_task_selection_pinned .then(|| active_task.to_owned()), - ); + )?; Ok(()) } @@ -167,6 +167,7 @@ impl App { self.selected_task_index = (self.selected_task_index + 1) % num_rows; self.task_list_scroll.select(Some(self.selected_task_index)); self.is_task_selection_pinned = true; + self.persist_active_task().ok(); } } @@ -180,6 +181,7 @@ impl App { .unwrap_or(num_rows - 1); self.task_list_scroll.select(Some(self.selected_task_index)); self.is_task_selection_pinned = true; + self.persist_active_task().ok(); } } @@ -816,7 +818,11 @@ fn update( app.is_task_selection_pinned = !app.is_task_selection_pinned; } Event::ToggleSidebar => { - app.has_sidebar = !app.has_sidebar; + let new_value = !app.preferences.is_task_list_visible(); + + app.preferences + .set_is_task_list_visible(Some(new_value)) + .ok(); } Event::ToggleHelpPopup => { app.showing_help_popup = !app.showing_help_popup; @@ -872,7 +878,7 @@ fn update( fn view(app: &mut App, f: &mut Frame) { let cols = app.size.pane_cols(); - let horizontal = if app.has_sidebar { + let horizontal = if app.preferences.is_task_list_visible() { Layout::horizontal([Constraint::Fill(1), Constraint::Length(cols)]) } else { Layout::horizontal([Constraint::Max(0), Constraint::Length(cols)]) @@ -886,7 +892,7 @@ fn view(app: &mut App, f: &mut Frame) { output_logs, &active_task, &app.section_focus, - app.has_sidebar, + app.preferences.is_task_list_visible(), ); let table_to_render = TaskTable::new(&app.tasks_by_status); diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index e58c7943e68d7..69285c183fb5a 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -40,6 +40,11 @@ impl PreferenceLoader { self.config.is_task_list_visible.unwrap_or(true) } + pub fn set_is_task_list_visible(&mut self, value: Option) -> Result<(), Error> { + self.config.is_task_list_visible = value; + self.flush_to_disk() + } + pub fn active_task(&self) -> Option<&str> { let active_task = self.config.active_task.as_deref()?; Some(active_task) From 4207f09c7f19589935cc9131960570d6a8d5daf9 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 15:17:21 -0700 Subject: [PATCH 14/22] WIP --- crates/turborepo-ui/src/tui/preferences.rs | 117 +-------------------- 1 file changed, 1 insertion(+), 116 deletions(-) diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 69285c183fb5a..9aa7287b69514 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -1,14 +1,6 @@ -use std::{ - fs::{self, File}, - io::{BufReader, Write}, -}; - use serde::{Deserialize, Serialize}; -use serde_json::{from_reader, json, Value}; use turbopath::AbsoluteSystemPathBuf; -use super::task::TasksByStatus; - const TUI_PREFERENCES_PATH_COMPONENTS: &[&str] = &[".turbo", "preferences", "tui.json"]; #[derive(Debug, thiserror::Error)] @@ -50,17 +42,13 @@ impl PreferenceLoader { Some(active_task) } - pub fn is_pinned_task_selection(&self) -> bool { - self.config.is_pinned_task_selection.unwrap_or(true) - } - pub fn set_active_task(&mut self, value: Option) -> Result<(), Error> { self.config.active_task = value; self.flush_to_disk() } fn flush_to_disk(&self) -> Result<(), Error> { - self.file_path.ensure_dir(); + self.file_path.ensure_dir()?; self.file_path .create_with_contents(serde_json::to_string_pretty(&self.config)?)?; @@ -75,13 +63,6 @@ pub struct Preferences { pub is_pinned_task_selection: Option, } -#[derive(Debug)] -pub enum PreferenceFields { - IsTaskListVisible, - ActiveTask, - PinnedTaskSelection, -} - impl Default for Preferences { fn default() -> Self { Self { @@ -91,99 +72,3 @@ impl Default for Preferences { } } } - -fn read_json(path: &AbsoluteSystemPathBuf) -> Preferences { - File::open(path) - .ok() - .and_then(|file| from_reader(BufReader::new(file)).ok()) - .unwrap_or_default() -} - -impl Preferences { - pub fn update_preference( - repo_root: &AbsoluteSystemPathBuf, - field: PreferenceFields, - new_value: Value, - ) -> Result<(), Box> { - // TODO: Clean these up, should be taken from constants - let preferences_dir = repo_root.join_components(&[".turbo", "preferences"]); - let preferences_file = repo_root.join_components(&[".turbo", "preferences", "tui.json"]); - - fs::create_dir_all(preferences_dir.as_std_path())?; - - let mut json: Value = if preferences_file.exists() { - let json_string = fs::read_to_string(&preferences_file)?; - serde_json::from_str(&json_string)? - } else { - json!({}) - }; - - // TODO: Is this really how to do this? No way, right? - match field { - PreferenceFields::IsTaskListVisible => { - json["is_task_list_visible"] = new_value; - } - PreferenceFields::ActiveTask => { - json["active_task"] = new_value; - } - PreferenceFields::PinnedTaskSelection => { - json["is_pinned_task_selection"] = new_value; - } - } - - let updated_json_string = serde_json::to_string_pretty(&json)?; - - let mut file = fs::File::create(&preferences_file)?; - file.write_all(updated_json_string.as_bytes())?; - - Ok(()) - } - - pub fn read_pinned_task_state(repo_root: &AbsoluteSystemPathBuf) -> bool { - let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); - - read_json(&preferences_file) - .is_pinned_task_selection - .unwrap_or(false) - } - - pub fn read_task_list_visibility(repo_root: &AbsoluteSystemPathBuf) -> bool { - let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); - - read_json(&preferences_file) - .is_task_list_visible - .unwrap_or(true) - } - - pub fn get_selected_task_index( - repo_root: &AbsoluteSystemPathBuf, - tasks_by_status: &TasksByStatus, - ) -> usize { - let preferences_file = repo_root.join_components(TUI_PREFERENCES_PATH_COMPONENTS); - - let selected_task_name = read_json(&preferences_file) - .active_task - .unwrap_or("".to_string()); - - match tasks_by_status - .task_names_in_displayed_order() - .position(|task_name| *task_name == selected_task_name) - { - Some(index) => index, - None => { - let _ = Self::update_preference( - repo_root, - PreferenceFields::PinnedTaskSelection, - serde_json::Value::Bool(false), - ); - - let _ = Self::update_preference( - repo_root, - PreferenceFields::ActiveTask, - serde_json::Value::String("".to_string()), - ); - 0 - } - } - } -} From aad46645c7aade54a9308d90ffd3c5ed942b7394 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 15:22:15 -0700 Subject: [PATCH 15/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 03accdc8292c3..dc429bcad7a54 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -56,7 +56,6 @@ pub struct App { task_list_scroll: TableState, selected_task_index: usize, is_task_selection_pinned: bool, - has_sidebar: bool, showing_help_popup: bool, done: bool, preferences: PreferenceLoader, @@ -105,7 +104,6 @@ impl App { selected_task_index, tasks_by_status: tasks_by_status.clone(), task_list_scroll: TableState::default().with_selected(selected_task_index), - has_sidebar: preferences.is_task_list_visible(), showing_help_popup: false, is_task_selection_pinned: preferences.active_task().is_some(), preferences, @@ -133,6 +131,12 @@ impl App { }) } + fn update_sidebar_toggle(&mut self) { + let value = Some(self.preferences.is_task_list_visible()); + + self.preferences.set_is_task_list_visible(value).ok(); + } + pub fn get_full_task(&self) -> Result<&TerminalOutput, Error> { let active_task = self.active_task()?; self.tasks @@ -818,11 +822,7 @@ fn update( app.is_task_selection_pinned = !app.is_task_selection_pinned; } Event::ToggleSidebar => { - let new_value = !app.preferences.is_task_list_visible(); - - app.preferences - .set_is_task_list_visible(Some(new_value)) - .ok(); + app.update_sidebar_toggle(); } Event::ToggleHelpPopup => { app.showing_help_popup = !app.showing_help_popup; From 2bf8f966f8f4dba845074973b91afb8bb5185ad2 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 15:36:12 -0700 Subject: [PATCH 16/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 8 ++++++-- crates/turborepo-ui/src/tui/preferences.rs | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index dc429bcad7a54..068e4e2bfa5d4 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -55,6 +55,7 @@ pub struct App { section_focus: LayoutSections, task_list_scroll: TableState, selected_task_index: usize, + // is_task_list_visible: bool, is_task_selection_pinned: bool, showing_help_popup: bool, done: bool, @@ -104,6 +105,7 @@ impl App { selected_task_index, tasks_by_status: tasks_by_status.clone(), task_list_scroll: TableState::default().with_selected(selected_task_index), + // is_task_list_visible: preferences.is_task_list_visible(), showing_help_popup: false, is_task_selection_pinned: preferences.active_task().is_some(), preferences, @@ -132,9 +134,10 @@ impl App { } fn update_sidebar_toggle(&mut self) { - let value = Some(self.preferences.is_task_list_visible()); + let value = !self.preferences.is_task_list_visible(); - self.preferences.set_is_task_list_visible(value).ok(); + // self.is_task_list_visible = value; + self.preferences.set_is_task_list_visible(Some(value)).ok(); } pub fn get_full_task(&self) -> Result<&TerminalOutput, Error> { @@ -756,6 +759,7 @@ fn cleanup( )?; let tasks_started = app.tasks_by_status.tasks_started(); app.persist_tasks(tasks_started)?; + app.preferences.flush_to_disk().ok(); crossterm::terminal::disable_raw_mode()?; terminal.show_cursor()?; // We can close the channel now that terminal is back restored to a normal state diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 9aa7287b69514..a7d0e085741de 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -34,7 +34,7 @@ impl PreferenceLoader { pub fn set_is_task_list_visible(&mut self, value: Option) -> Result<(), Error> { self.config.is_task_list_visible = value; - self.flush_to_disk() + Ok(()) } pub fn active_task(&self) -> Option<&str> { @@ -44,10 +44,10 @@ impl PreferenceLoader { pub fn set_active_task(&mut self, value: Option) -> Result<(), Error> { self.config.active_task = value; - self.flush_to_disk() + Ok(()) } - fn flush_to_disk(&self) -> Result<(), Error> { + pub fn flush_to_disk(&self) -> Result<(), Error> { self.file_path.ensure_dir()?; self.file_path .create_with_contents(serde_json::to_string_pretty(&self.config)?)?; From 0f8f92dd1c3a7a6bbc0b0f0255960bbc72cb3932 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 16:18:39 -0700 Subject: [PATCH 17/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 120 +++++++++++++++++---- crates/turborepo-ui/src/tui/preferences.rs | 6 ++ 2 files changed, 106 insertions(+), 20 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 068e4e2bfa5d4..79b0b07b9c678 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -914,16 +914,23 @@ fn view(app: &mut App, f: &mut Frame) { #[cfg(test)] mod test { + use tempfile::tempdir; + use turbopath::AbsoluteSystemPathBuf; + use super::*; use crate::tui::event::CacheResult; #[test] - fn test_scroll() { + fn test_scroll() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App = App::new( 100, 100, vec!["foo".to_string(), "bar".to_string(), "baz".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); assert_eq!( app.task_list_scroll.selected(), @@ -957,15 +964,20 @@ mod test { Some(2), "scroll stays in bounds" ); + Ok(()) } #[test] fn test_selection_follows() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); assert_eq!(app.task_list_scroll.selected(), Some(1), "selected b"); @@ -984,11 +996,15 @@ mod test { #[test] fn test_restart_task() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); app.next(); @@ -1051,11 +1067,15 @@ mod test { #[test] fn test_selection_stable() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); app.next(); @@ -1098,11 +1118,15 @@ mod test { #[test] fn test_forward_stdin() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App> = App::new( 100, 100, vec!["a".to_string(), "b".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); assert_eq!(app.task_list_scroll.selected(), Some(1), "selected b"); @@ -1136,11 +1160,15 @@ mod test { #[test] fn test_interact() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App> = App::new( 100, 100, vec!["a".to_string(), "b".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); assert!(!app.is_focusing_pane(), "app starts focused on table"); app.insert_stdin("a", Some(Vec::new()))?; @@ -1163,11 +1191,15 @@ mod test { #[test] fn test_task_status() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App> = App::new( 100, 100, vec!["a".to_string(), "b".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); assert_eq!(app.task_list_scroll.selected(), Some(1), "selected b"); @@ -1185,11 +1217,15 @@ mod test { #[test] fn test_restarting_task_no_scroll() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); assert_eq!(app.task_list_scroll.selected(), Some(0), "selected a"); assert_eq!(app.tasks_by_status.task_name(0)?, "a", "selected a"); @@ -1216,11 +1252,15 @@ mod test { #[test] fn test_restarting_task() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); assert_eq!(app.task_list_scroll.selected(), Some(1), "selected b"); @@ -1248,11 +1288,15 @@ mod test { #[test] fn test_resize() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App> = App::new( 20, 24, vec!["a".to_string(), "b".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); let pane_rows = app.size.pane_rows(); let pane_cols = app.size.pane_cols(); @@ -1284,11 +1328,15 @@ mod test { #[test] fn test_update_empty_task_list() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); app.update_tasks(Vec::new())?; @@ -1299,11 +1347,15 @@ mod test { #[test] fn test_restart_missing_task() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); app.restart_tasks(vec!["d".to_string()])?; @@ -1316,11 +1368,15 @@ mod test { #[test] fn test_search_backspace_exits_search() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "b".to_string(), "c".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.enter_search()?; assert!(matches!(app.section_focus, LayoutSections::Search { .. })); @@ -1338,11 +1394,15 @@ mod test { #[test] fn test_search_moves_with_typing() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.enter_search()?; app.search_enter_char('a')?; @@ -1362,11 +1422,15 @@ mod test { #[test] fn test_search_scroll() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.enter_search()?; app.search_enter_char('b')?; @@ -1390,11 +1454,15 @@ mod test { #[test] fn test_exit_search_restore_selection() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "abc".to_string(), "b".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); assert_eq!(app.active_task()?, "abc"); @@ -1410,11 +1478,15 @@ mod test { #[test] fn test_exit_search_keep_selection() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "abc".to_string(), "b".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.next(); assert_eq!(app.active_task()?, "abc"); @@ -1430,11 +1502,15 @@ mod test { #[test] fn test_select_update_task_removes_task() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.enter_search()?; app.search_enter_char('b')?; @@ -1447,11 +1523,15 @@ mod test { #[test] fn test_select_restart_tasks_reorders_tasks() -> Result<(), Error> { + let repo_root_tmp = tempdir()?; + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let mut app: App<()> = App::new( 100, 100, vec!["a".to_string(), "ab".to_string(), "abc".to_string()], - &AbsoluteSystemPathBuf::default(), + PreferenceLoader::new(&repo_root)?, ); app.enter_search()?; app.search_enter_char('b')?; diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index a7d0e085741de..989e8f871054e 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -72,3 +72,9 @@ impl Default for Preferences { } } } + +#[test] +fn default_preferences() { + let preferences = Preferences::default(); + assert!(preferences.is_task_list_visible.unwrap(), "true"); +} From 8168cb2a23659c951a8a81372203ca17ac517504 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 21:45:32 -0700 Subject: [PATCH 18/22] WIP --- crates/turborepo-ui/src/tui/preferences.rs | 106 ++++++++++++++++++++- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 989e8f871054e..048f0fe3458ec 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -73,8 +73,106 @@ impl Default for Preferences { } } -#[test] -fn default_preferences() { - let preferences = Preferences::default(); - assert!(preferences.is_task_list_visible.unwrap(), "true"); +#[cfg(test)] +mod test { + use tempfile::tempdir; + + use super::*; + + fn create_loader(repo_root: AbsoluteSystemPathBuf) -> PreferenceLoader { + PreferenceLoader::new(&repo_root).expect("Failed to create PreferenceLoader") + } + + #[test] + fn default_preferences() { + let preferences = Preferences::default(); + assert_eq!(preferences.active_task, None); + assert_eq!(preferences.is_task_list_visible, Some(true)); + assert_eq!(preferences.is_pinned_task_selection, Some(false)); + } + + #[test] + fn task_list_visible_when_no_preferences() { + let repo_root_tmp = tempdir().expect("Failed to create tempdir"); + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + let loader = create_loader(repo_root); + + let visibility = PreferenceLoader::is_task_list_visible(&loader); + assert!(visibility); + } + + #[test] + fn task_is_none_when_no_preferences() { + let repo_root_tmp = tempdir().expect("Failed to create tempdir"); + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + + let loader = create_loader(repo_root); + + let task = PreferenceLoader::active_task(&loader); + assert_eq!(task, None); + } + + #[test] + fn sets_active_task() { + let repo_root_tmp = tempdir().expect("Failed to create tempdir"); + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + + let loader = create_loader(repo_root.clone()); + + loader + .file_path + .ensure_dir() + .expect("Failed to create directory"); + + let preferences = Preferences { + active_task: Some("web#dev".to_owned()), + is_task_list_visible: Some(false), + is_pinned_task_selection: Some(true), + }; + + loader + .file_path + .create_with_contents( + serde_json::to_string_pretty(&preferences) + .expect("Failed to serialize preferences"), + ) + .expect("Failed to create file"); + + let task = PreferenceLoader::new(&repo_root).expect("Failed to create PreferenceLoader"); + assert_eq!(task.active_task(), Some("web#dev")); + } + + #[test] + fn sets_task_list_visibility() { + let repo_root_tmp = tempdir().expect("Failed to create tempdir"); + let repo_root = AbsoluteSystemPathBuf::try_from(repo_root_tmp.path()) + .expect("Failed to create AbsoluteSystemPathBuf"); + + let loader = create_loader(repo_root.clone()); + + loader + .file_path + .ensure_dir() + .expect("Failed to create directory"); + + let preferences = Preferences { + active_task: Some("web#dev".to_owned()), + is_task_list_visible: Some(false), + is_pinned_task_selection: Some(true), + }; + + loader + .file_path + .create_with_contents( + serde_json::to_string_pretty(&preferences) + .expect("Failed to serialize preferences"), + ) + .expect("Failed to create file"); + + let task = PreferenceLoader::new(&repo_root).expect("Failed to create PreferenceLoader"); + assert!(!task.is_task_list_visible()); + } } From 4a8c01e826988895e8d428cd1a3122c1650ab62b Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 22:06:47 -0700 Subject: [PATCH 19/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 79b0b07b9c678..6dafe222acc56 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -55,7 +55,6 @@ pub struct App { section_focus: LayoutSections, task_list_scroll: TableState, selected_task_index: usize, - // is_task_list_visible: bool, is_task_selection_pinned: bool, showing_help_popup: bool, done: bool, @@ -105,7 +104,6 @@ impl App { selected_task_index, tasks_by_status: tasks_by_status.clone(), task_list_scroll: TableState::default().with_selected(selected_task_index), - // is_task_list_visible: preferences.is_task_list_visible(), showing_help_popup: false, is_task_selection_pinned: preferences.active_task().is_some(), preferences, @@ -135,11 +133,15 @@ impl App { fn update_sidebar_toggle(&mut self) { let value = !self.preferences.is_task_list_visible(); - - // self.is_task_list_visible = value; self.preferences.set_is_task_list_visible(Some(value)).ok(); } + fn update_task_selection_pinned_state(&mut self) { + // Preferences assume a pinned state when there is an active task. + // This `None` creates "un-pinned-ness" on the next TUI startup. + self.preferences.set_active_task(None).ok(); + } + pub fn get_full_task(&self) -> Result<&TerminalOutput, Error> { let active_task = self.active_task()?; self.tasks @@ -823,7 +825,7 @@ fn update( app.interact()?; } Event::TogglePinnedTask => { - app.is_task_selection_pinned = !app.is_task_selection_pinned; + app.update_task_selection_pinned_state(); } Event::ToggleSidebar => { app.update_sidebar_toggle(); From 04f715d1b0681bf8ec6e547bebd9713ed639878e Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 22:18:34 -0700 Subject: [PATCH 20/22] WIP --- crates/turborepo-ui/src/tui/preferences.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 048f0fe3458ec..92b141978bc7e 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -60,7 +60,6 @@ impl PreferenceLoader { pub struct Preferences { pub is_task_list_visible: Option, pub active_task: Option, - pub is_pinned_task_selection: Option, } impl Default for Preferences { @@ -68,7 +67,6 @@ impl Default for Preferences { Self { active_task: None, is_task_list_visible: Some(true), - is_pinned_task_selection: Some(false), } } } @@ -88,7 +86,6 @@ mod test { let preferences = Preferences::default(); assert_eq!(preferences.active_task, None); assert_eq!(preferences.is_task_list_visible, Some(true)); - assert_eq!(preferences.is_pinned_task_selection, Some(false)); } #[test] @@ -130,7 +127,6 @@ mod test { let preferences = Preferences { active_task: Some("web#dev".to_owned()), is_task_list_visible: Some(false), - is_pinned_task_selection: Some(true), }; loader @@ -161,7 +157,6 @@ mod test { let preferences = Preferences { active_task: Some("web#dev".to_owned()), is_task_list_visible: Some(false), - is_pinned_task_selection: Some(true), }; loader From 00b6b0e52c5a73539c4de7040d31c2e6f1994f74 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 22:20:18 -0700 Subject: [PATCH 21/22] Update crates/turborepo-ui/src/tui/app.rs --- crates/turborepo-ui/src/tui/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 6dafe222acc56..2f5f4d2ec4375 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -82,7 +82,7 @@ impl App { let pane_rows = size.pane_rows(); let pane_cols = size.pane_cols(); - // Attempt to load previous selection. If not, go to 0. + // Attempt to load previous selection. If there isn't one, go to index 0. let selected_task_index = preferences .active_task() .and_then(|active_task| tasks_by_status.active_index(active_task)) From 63b3e77b19d6f94c8a6bf1f5d00447f5eff5e583 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 16 Dec 2024 22:32:10 -0700 Subject: [PATCH 22/22] WIP --- crates/turborepo-ui/src/tui/app.rs | 11 ++++++----- crates/turborepo-ui/src/tui/event.rs | 2 +- crates/turborepo-ui/src/tui/mod.rs | 2 +- crates/turborepo-ui/src/tui/preferences.rs | 3 +-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 6dafe222acc56..bf01ef79c6fda 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -102,7 +102,7 @@ impl App { }) .collect(), selected_task_index, - tasks_by_status: tasks_by_status.clone(), + tasks_by_status, task_list_scroll: TableState::default().with_selected(selected_task_index), showing_help_popup: false, is_task_selection_pinned: preferences.active_task().is_some(), @@ -133,13 +133,14 @@ impl App { fn update_sidebar_toggle(&mut self) { let value = !self.preferences.is_task_list_visible(); - self.preferences.set_is_task_list_visible(Some(value)).ok(); + self.preferences.set_is_task_list_visible(Some(value)); } - fn update_task_selection_pinned_state(&mut self) { + fn update_task_selection_pinned_state(&mut self) -> Result<(), Error> { // Preferences assume a pinned state when there is an active task. // This `None` creates "un-pinned-ness" on the next TUI startup. - self.preferences.set_active_task(None).ok(); + self.preferences.set_active_task(None)?; + Ok(()) } pub fn get_full_task(&self) -> Result<&TerminalOutput, Error> { @@ -825,7 +826,7 @@ fn update( app.interact()?; } Event::TogglePinnedTask => { - app.update_task_selection_pinned_state(); + app.update_task_selection_pinned_state()?; } Event::ToggleSidebar => { app.update_sidebar_toggle(); diff --git a/crates/turborepo-ui/src/tui/event.rs b/crates/turborepo-ui/src/tui/event.rs index f24a4621c1c4c..775a7ef4bcc17 100644 --- a/crates/turborepo-ui/src/tui/event.rs +++ b/crates/turborepo-ui/src/tui/event.rs @@ -50,9 +50,9 @@ pub enum Event { rows: u16, cols: u16, }, - TogglePinnedTask, ToggleSidebar, ToggleHelpPopup, + TogglePinnedTask, SearchEnter, SearchExit { restore_scroll: bool, diff --git a/crates/turborepo-ui/src/tui/mod.rs b/crates/turborepo-ui/src/tui/mod.rs index 274c6edfadc3c..577ea1b2eece6 100644 --- a/crates/turborepo-ui/src/tui/mod.rs +++ b/crates/turborepo-ui/src/tui/mod.rs @@ -37,6 +37,6 @@ pub enum Error { Stdin { name: String, e: std::io::Error }, #[error(transparent)] Io(#[from] std::io::Error), - #[error("Unable to persist preferences")] + #[error("Unable to persist preferences.")] Preferences(#[from] preferences::Error), } diff --git a/crates/turborepo-ui/src/tui/preferences.rs b/crates/turborepo-ui/src/tui/preferences.rs index 92b141978bc7e..54e9ce023ebb1 100644 --- a/crates/turborepo-ui/src/tui/preferences.rs +++ b/crates/turborepo-ui/src/tui/preferences.rs @@ -32,9 +32,8 @@ impl PreferenceLoader { self.config.is_task_list_visible.unwrap_or(true) } - pub fn set_is_task_list_visible(&mut self, value: Option) -> Result<(), Error> { + pub fn set_is_task_list_visible(&mut self, value: Option) { self.config.is_task_list_visible = value; - Ok(()) } pub fn active_task(&self) -> Option<&str> {