diff --git a/Cargo.lock b/Cargo.lock index d0804ad5..51661080 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,8 +6,7 @@ version = 4 name = "about" version = "0.1.0" dependencies = [ - "ansi_term", - "chrono", + "rand 0.9.0", "zellij-tile", ] @@ -44,7 +43,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.34", ] [[package]] @@ -458,7 +457,7 @@ dependencies = [ "cap-primitives", "cap-std", "io-lifetimes 2.0.3", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -486,7 +485,7 @@ dependencies = [ "ipnet", "maybe-owned", "rustix 0.38.44", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "winx", ] @@ -497,7 +496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea13372b49df066d1ae654e5c6e41799c1efd9f6b36794b921e877ea4037977" dependencies = [ "ambient-authority", - "rand", + "rand 0.8.5", ] [[package]] @@ -1165,7 +1164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1509,6 +1508,18 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "ghost" version = "0.1.4" @@ -1834,7 +1845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" dependencies = [ "io-lifetimes 2.0.3", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2328,7 +2339,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" dependencies = [ - "rand", + "rand 0.8.5", ] [[package]] @@ -2712,7 +2723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -2958,8 +2969,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.3", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.0", + "zerocopy 0.8.17", ] [[package]] @@ -2969,7 +2991,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.0", ] [[package]] @@ -2978,7 +3010,17 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom", + "getrandom 0.2.10", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.17", ] [[package]] @@ -3029,7 +3071,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom", + "getrandom 0.2.10", "redox_syscall 0.2.13", "thiserror", ] @@ -3134,7 +3176,7 @@ dependencies = [ "libc", "linux-raw-sys 0.4.15", "once_cell", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3458,7 +3500,7 @@ dependencies = [ "ansi_term", "colored", "lazy_static", - "rand", + "rand 0.8.5", "regex", "serde", "serde_json", @@ -4052,7 +4094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "atomic", - "getrandom", + "getrandom 0.2.10", "serde", ] @@ -4146,6 +4188,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.87" @@ -4617,7 +4668,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5a5e0adf7eed68976410def849a4bdab6f6e9f6163f152de9cb89deea9e60b" dependencies = [ - "getrandom", + "getrandom 0.2.10", "mac_address", "once_cell", "sha2", @@ -5030,6 +5081,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "wit-parser" version = "0.221.2" @@ -5120,7 +5180,7 @@ dependencies = [ "insta", "log", "names", - "rand", + "rand 0.8.5", "regex", "ssh2", "suggest", @@ -5255,7 +5315,16 @@ version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.34", +] + +[[package]] +name = "zerocopy" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" +dependencies = [ + "zerocopy-derive 0.8.17", ] [[package]] @@ -5269,6 +5338,17 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "zeroize" version = "1.5.5" diff --git a/default-plugins/about/Cargo.toml b/default-plugins/about/Cargo.toml index 66a79ac2..536617d6 100644 --- a/default-plugins/about/Cargo.toml +++ b/default-plugins/about/Cargo.toml @@ -6,6 +6,5 @@ edition = "2021" license = "MIT" [dependencies] -ansi_term = "0.12.1" zellij-tile = { path = "../../zellij-tile" } -chrono = "0.4.0" +rand = "0.9.0" diff --git a/default-plugins/about/src/main.rs b/default-plugins/about/src/main.rs index bd7c28ea..87af9ffd 100644 --- a/default-plugins/about/src/main.rs +++ b/default-plugins/about/src/main.rs @@ -1,11 +1,15 @@ mod active_component; mod pages; +mod tips; use zellij_tile::prelude::*; use pages::Page; +use rand::prelude::*; +use rand::rng; use std::cell::RefCell; use std::collections::BTreeMap; use std::rc::Rc; +use tips::MAX_TIP_INDEX; const UI_ROWS: usize = 20; const UI_COLUMNS: usize = 90; @@ -20,6 +24,10 @@ struct App { tab_columns: usize, own_plugin_id: Option, is_release_notes: bool, + is_startup_tip: bool, + tip_index: usize, + waiting_for_config_to_be_written: bool, + error: Option, } impl Default for App { @@ -32,6 +40,7 @@ impl Default for App { link_executable.clone(), "".to_owned(), base_mode.clone(), + false, ), link_executable, zellij_version, @@ -40,6 +49,10 @@ impl Default for App { tab_columns: 0, own_plugin_id: None, is_release_notes: false, + is_startup_tip: false, + tip_index: 0, + waiting_for_config_to_be_written: false, + error: None, } } } @@ -52,27 +65,62 @@ impl ZellijPlugin for App { .get("is_release_notes") .map(|v| v == "true") .unwrap_or(false); + self.is_startup_tip = configuration + .get("is_startup_tip") + .map(|v| v == "true") + .unwrap_or(false); subscribe(&[ EventType::Key, EventType::Mouse, EventType::ModeUpdate, EventType::RunCommandResult, EventType::TabUpdate, + EventType::FailedToWriteConfigToDisk, + EventType::ConfigWasWrittenToDisk, ]); let own_plugin_id = get_plugin_ids().plugin_id; self.own_plugin_id = Some(own_plugin_id); *self.zellij_version.borrow_mut() = get_zellij_version(); self.change_own_title(); self.query_link_executable(); - self.active_page = Page::new_main_screen( - self.link_executable.clone(), - self.zellij_version.borrow().clone(), - self.base_mode.clone(), - ); + self.active_page = if self.is_startup_tip { + let mut rng = rng(); + self.tip_index = rng.random_range(0..=MAX_TIP_INDEX); + Page::new_tip_screen( + self.link_executable.clone(), + self.base_mode.clone(), + self.tip_index, + ) + } else { + Page::new_main_screen( + self.link_executable.clone(), + self.zellij_version.borrow().clone(), + self.base_mode.clone(), + self.is_release_notes, + ) + }; } fn update(&mut self, event: Event) -> bool { let mut should_render = false; match event { + Event::FailedToWriteConfigToDisk(file_path) => { + if self.waiting_for_config_to_be_written { + let error = match file_path { + Some(file_path) => { + format!("Failed to write config to disk at: {}", file_path) + }, + None => format!("Failed to write config to disk."), + }; + eprintln!("{}", error); + self.error = Some(error); + should_render = true; + } + }, + Event::ConfigWasWrittenToDisk => { + if self.waiting_for_config_to_be_written { + close_self(); + } + }, Event::TabUpdate(tab_info) => { self.center_own_pane(tab_info); }, @@ -98,14 +146,19 @@ impl ZellijPlugin for App { } }, Event::Key(key) => { - should_render = self.handle_key(key); + if let Some(_error) = self.error.take() { + // dismiss error on any key + should_render = true; + } else { + should_render = self.handle_key(key); + } }, _ => {}, } should_render } fn render(&mut self, rows: usize, cols: usize) { - self.active_page.render(rows, cols); + self.active_page.render(rows, cols, &self.error); } } @@ -162,11 +215,24 @@ impl App { } pub fn handle_key(&mut self, key: KeyWithModifier) -> bool { let mut should_render = false; - if key.bare_key == BareKey::Enter && key.has_no_modifiers() { + if key.bare_key == BareKey::Up && key.has_no_modifiers() && self.is_startup_tip { + self.previous_tip(); + should_render = true; + } else if key.bare_key == BareKey::Down && key.has_no_modifiers() && self.is_startup_tip { + self.next_tip(); + should_render = true; + } else if key.bare_key == BareKey::Enter && key.has_no_modifiers() { if let Some(new_page) = self.active_page.handle_selection() { self.active_page = new_page; should_render = true; } + } else if key.bare_key == BareKey::Char('c') + && key.has_modifiers(&[KeyModifier::Ctrl]) + && self.is_startup_tip + { + self.waiting_for_config_to_be_written = true; + let save_configuration = true; + reconfigure("show_startup_tips false".to_owned(), save_configuration); } else if key.bare_key == BareKey::Esc && key.has_no_modifiers() { if self.active_page.is_main_screen { close_self(); @@ -175,9 +241,21 @@ impl App { self.link_executable.clone(), self.zellij_version.borrow().clone(), self.base_mode.clone(), + self.is_release_notes, ); should_render = true; } + } else if key.bare_key == BareKey::Char('?') + && !self.is_release_notes + && !self.is_startup_tip + { + self.is_startup_tip = true; + self.active_page = Page::new_tip_screen( + self.link_executable.clone(), + self.base_mode.clone(), + self.tip_index, + ); + should_render = true; } else { should_render = self.active_page.handle_key(key); } @@ -208,4 +286,28 @@ impl App { } } } + fn previous_tip(&mut self) { + if self.tip_index == 0 { + self.tip_index = MAX_TIP_INDEX; + } else { + self.tip_index = self.tip_index.saturating_sub(1); + } + self.active_page = Page::new_tip_screen( + self.link_executable.clone(), + self.base_mode.clone(), + self.tip_index, + ); + } + fn next_tip(&mut self) { + if self.tip_index == MAX_TIP_INDEX { + self.tip_index = 0; + } else { + self.tip_index += 1; + } + self.active_page = Page::new_tip_screen( + self.link_executable.clone(), + self.base_mode.clone(), + self.tip_index, + ); + } } diff --git a/default-plugins/about/src/pages.rs b/default-plugins/about/src/pages.rs index 2951c1b0..9bf82200 100644 --- a/default-plugins/about/src/pages.rs +++ b/default-plugins/about/src/pages.rs @@ -20,10 +20,11 @@ impl Page { link_executable: Rc>, zellij_version: String, base_mode: Rc>, + is_release_notes: bool, ) -> Self { Page::new() .main_screen() - .with_title(main_screen_title(zellij_version.clone())) + .with_title(main_screen_title(zellij_version.clone(), is_release_notes)) .with_bulletin_list(BulletinList::new(whats_new_title()).with_items(vec![ ActiveComponent::new(TextOrCustomRender::Text(main_menu_item( "Stacked Resize", @@ -102,9 +103,15 @@ impl Page { link_executable.clone(), )), ])]) - .with_help(Box::new(|hovering_over_link, menu_item_is_selected| { - main_screen_help_text(hovering_over_link, menu_item_is_selected) - })) + .with_help(if is_release_notes { + Box::new(|hovering_over_link, menu_item_is_selected| { + release_notes_main_help(hovering_over_link, menu_item_is_selected) + }) + } else { + Box::new(|hovering_over_link, menu_item_is_selected| { + main_screen_help_text(hovering_over_link, menu_item_is_selected) + }) + }) } pub fn new_stacked_resize(link_executable: Rc>) -> Page { Page::new() @@ -543,7 +550,7 @@ impl Page { row_count += self.components_to_render.len(); row_count } - pub fn render(&mut self, rows: usize, columns: usize) { + pub fn render(&mut self, rows: usize, columns: usize, error: &Option) { let base_x = columns.saturating_sub(self.ui_column_count()) / 2; let base_y = rows.saturating_sub(self.ui_row_count()) / 2; let mut current_y = base_y; @@ -562,12 +569,23 @@ impl Page { RenderedComponent::HelpText(_) => true, _ => false, }; + if is_help { + if let Some(error) = error { + render_error(error, rows); + continue; + } + } let y = if is_help { rows } else { current_y }; + let columns = if is_help { + columns + } else { + columns.saturating_sub(base_x * 2) + }; let rendered_rows = rendered_component.render( base_x, y, rows, - columns.saturating_sub(base_x * 2), + columns, self.hovering_over_link, self.menu_item_is_selected, ); @@ -576,6 +594,16 @@ impl Page { } } +fn render_error(error: &str, y: usize) { + print_text_with_coordinates( + Text::new(format!("ERROR: {}", error)).color_range(3, ..), + 0, + y, + None, + None, + ); +} + fn changelog_link_unselected(version: String) -> Text { let full_changelog_text = format!( "https://github.com/zellij-org/zellij/releases/tag/v{}", @@ -647,12 +675,38 @@ fn whats_new_title() -> Text { Text::new("What's new?") } -fn main_screen_title(version: String) -> Text { - let title_text = format!("Hi there, welcome to Zellij {}!", &version); - Text::new(title_text).color_range(2, 21..=27 + version.chars().count()) +fn main_screen_title(version: String, is_release_notes: bool) -> Text { + if is_release_notes { + let title_text = format!("Hi there, welcome to Zellij {}!", &version); + Text::new(title_text).color_range(2, 21..=27 + version.chars().count()) + } else { + let title_text = format!("Zellij {}", &version); + Text::new(title_text).color_range(2, ..) + } } fn main_screen_help_text(hovering_over_link: bool, menu_item_is_selected: bool) -> Text { + if hovering_over_link { + let help_text = format!("Help: Click or Shift-Click to open in browser"); + Text::new(help_text) + .color_range(3, 6..=10) + .color_range(3, 15..=25) + } else if menu_item_is_selected { + let help_text = format!("Help: <↓↑> - Navigate, - Learn More, - Dismiss"); + Text::new(help_text) + .color_range(1, 6..=9) + .color_range(1, 23..=29) + .color_range(1, 45..=49) + } else { + let help_text = format!("Help: <↓↑> - Navigate, - Dismiss, - Usage Tips"); + Text::new(help_text) + .color_range(1, 6..=9) + .color_range(1, 23..=27) + .color_range(1, 40..=42) + } +} + +fn release_notes_main_help(hovering_over_link: bool, menu_item_is_selected: bool) -> Text { if hovering_over_link { let help_text = format!("Help: Click or Shift-Click to open in browser"); Text::new(help_text) diff --git a/default-plugins/about/src/tips.rs b/default-plugins/about/src/tips.rs new file mode 100644 index 00000000..3a594e8d --- /dev/null +++ b/default-plugins/about/src/tips.rs @@ -0,0 +1,852 @@ +use zellij_tile::prelude::*; + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::active_component::{ActiveComponent, ClickAction}; +use crate::pages::{BulletinList, ComponentLine, Page, TextOrCustomRender}; + +pub const MAX_TIP_INDEX: usize = 11; + +impl Page { + pub fn new_tip_screen( + link_executable: Rc>, + base_mode: Rc>, + tip_index: usize, + ) -> Self { + if tip_index == 0 { + Page::tip_1(link_executable) + } else if tip_index == 1 { + Page::tip_2(link_executable, base_mode) + } else if tip_index == 2 { + Page::tip_3(link_executable) + } else if tip_index == 3 { + Page::tip_4(link_executable, base_mode) + } else if tip_index == 4 { + Page::tip_5(link_executable) + } else if tip_index == 5 { + Page::tip_6(link_executable, base_mode) + } else if tip_index == 6 { + Page::tip_7(link_executable) + } else if tip_index == 7 { + Page::tip_8(link_executable) + } else if tip_index == 8 { + Page::tip_9(link_executable) + } else if tip_index == 9 { + Page::tip_10(link_executable, base_mode) + } else if tip_index == 10 { + Page::tip_11(link_executable) + } else if tip_index == 11 { + Page::tip_12(link_executable, base_mode) + } else { + Page::tip_1(link_executable) + } + } + pub fn tip_1(link_executable: Rc>) -> Self { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #1").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Check out the Zellij screencasts/tutorials to learn how to better take advantage") + )) + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("of all the Zellij features. Learn about basic usage, layouts, sessions and more!") + )) + ]) + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new("Follow this link: ").color_range(2, ..))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://zellij.dev/screencasts"))) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(screencasts_link_selected()), + Box::new(screencasts_link_selected_len()), + )) + .with_left_click_action(ClickAction::new_open_link( + format!("https://zellij.dev/screencasts"), + link_executable.clone(), + )), + ])]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_2(link_executable: Rc>, base_mode: Rc>) -> Self { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #2").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("You can open the terminal contents in your $EDITOR, allowing you to search") + .color_range(2, 43..=49) + )) + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("through them, copy to your clipboard or even save them for later.") + )) + ]) + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + match *base_mode.borrow() { + InputMode::Locked => { + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("While focused on a terminal pane: Ctrl g + s + e") + .color_range(0, 34..=39) + .color_indices(0, vec![43, 47]) + )) + }, + _ => { + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("While focused on a terminal pane: Ctrl s + e") + .color_range(0, 34..=39) + .color_indices(0, vec![43]) + )) + } + } + ])]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_3(link_executable: Rc>) -> Self { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #3").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Want to make your floating pane bigger?"), + ))]), + ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text( + Text::new( + "You can switch to the ENLARGED layout with Alt ] while focused on it.", + ) + .color_range(2, 22..=29) + .color_range(0, 43..=47), + ))]), + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + fn tip_4(link_executable: Rc>, base_mode: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij tip #4").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text( + Text::new("It's possible to \"pin\" a floating pane so that it will always"), + ))]), + ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text( + Text::new("be visible even if floating panes are hidden."), + ))]), + ]) + .with_bulletin_list( + BulletinList::new( + Text::new(format!("Floating panes can be \"pinned\": ")).color_range(2, ..), + ) + .with_items(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new(format!("With a mouse click on their top right corner")) + .color_range(3, 7..=17), + )), + ActiveComponent::new(TextOrCustomRender::Text(match *base_mode.borrow() { + InputMode::Locked => Text::new(format!("With Ctrl g + p + i")) + .color_range(3, 5..=10) + .color_range(3, 14..15) + .color_range(3, 18..19), + _ => Text::new("With Ctrl p + i") + .color_range(3, 5..=10) + .color_range(3, 14..15), + })), + ]), + ) + .with_paragraph(vec![ + ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text( + Text::new("A great use case for these is to tail log files or to show"), + ))]), + ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text( + Text::new(format!( + "real-time compiler output while working in other panes." + )), + ))]), + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_5(link_executable: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #5").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new("Panes can be resized into stacks to be managed easier."))), + ]), + ]) + .with_bulletin_list(BulletinList::new(Text::new("To try it out:").color_range(2, ..)) + .with_items(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Hide this pane with Alt f (you can bring it back with Alt f again)") + .color_range(3, 20..=24) + .color_range(3, 54..=58) + )), + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Open 4-5 panes with Alt n") + .color_range(3, 20..=24) + )), + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Press Alt + until you reach full screen") + .color_range(3, 6..=10) + )), + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Press Alt - until you are back at the original state") + .color_range(3, 6..=10) + )), + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("You can always snap back to the built-in swap layouts with Alt <[]>") + .color_range(3, 59..=61) + .color_range(3, 64..=65) + )), + ]) + ) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("To disable this behavior, add stacked_resize false to the Zellij Configuration") + .color_range(3, 30..=49) + )), + ]) + ]) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("For more details, see: ") + .color_range(2, ..) + )), + ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://zellij.dev/screencasts/stacked-resize"))) + .with_hover(TextOrCustomRender::CustomRender(Box::new(stacked_resize_screencast_link_selected), Box::new(stacked_resize_screencast_link_selected_len))) + .with_left_click_action(ClickAction::new_open_link("https://zellij.dev/screencasts/stacked-resize".to_owned(), link_executable.clone())) + ]) + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_6(link_executable: Rc>, base_mode: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #6").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new("Are the Zellij keybindings colliding with other applications for you?"))) + ]), + ]) + .with_bulletin_list(BulletinList::new(Text::new("Check out the non-colliding keybindings preset:")) + .with_items(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + match *base_mode.borrow() { + InputMode::Locked => { + Text::new("Open the Zellij configuration with Ctrl g + o + c") + .color_range(3, 35..=40) + .color_indices(3, vec![44, 48]) + }, + _ => { + Text::new("Open the Zellij configuration with Ctrl o + c") + .color_range(3, 35..=40) + .color_indices(3, vec![44]) + } + } + )), + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Press TAB to go to Chagne Mode Behavior") + .color_range(3, 6..=9) + )), + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Select non-colliding temporarily with ENTER or permanently with Ctrl a") + .color_range(3, 38..=42) + .color_range(3, 64..=69) + )), + ]) + ) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("For more details, see: ") + .color_range(2, ..) + )), + ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://zellij.dev/tutorials/colliding-keybindings"))) + .with_hover(TextOrCustomRender::CustomRender(Box::new(colliding_keybindings_link_selected), Box::new(colliding_keybindings_link_selected_len))) + .with_left_click_action(ClickAction::new_open_link("https://zellij.dev/tutorials/colliding-keybindings".to_owned(), link_executable.clone())) + ]) + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_7(link_executable: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #7").color_range(0, ..)) + .with_paragraph(vec![ComponentLine::new(vec![ActiveComponent::new( + TextOrCustomRender::Text(Text::new( + "Want to customize the appearance and colors of Zellij?", + )), + )])]) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Check out the built-in themes: ").color_range(2, ..), + )), + ActiveComponent::new(TextOrCustomRender::Text(Text::new( + "https://zellij.dev/documentation/theme-list", + ))) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(theme_list_selected), + Box::new(theme_list_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://zellij.dev/documentation/theme-list".to_owned(), + link_executable.clone(), + )), + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Or create your own theme: ").color_range(2, ..), + )), + ActiveComponent::new(TextOrCustomRender::Text(Text::new( + "https://zellij.dev/documentation/themes", + ))) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(theme_link_selected), + Box::new(theme_link_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://zellij.dev/documentation/themes".to_owned(), + link_executable.clone(), + )), + ]), + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_8(link_executable: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #8").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("If you change the pane focus with Alt + <←↓↑→> or Alt + beyond the") + .color_range(0, 34..=36) + .color_range(2, 40..=45) + .color_range(0, 50..=52) + .color_range(2, 56..=60) + )) + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new("right or left side of the screen, the next or previous tab will be focused."))) + ]), + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_9(link_executable: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #9").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("For plugins, integrations and tutorials created by the community, check out the") + )) + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Awesome-zellij repository: ") + .color_range(2, ..=39) + )), + ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://github.com/zellij-org/awesome-zellij"))) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(awesome_zellij_link_text_selected), + Box::new(awesome_zellij_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/zellij-org/awesome-zellij".to_owned(), + link_executable.clone(), + )), + ]), + ]) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("For community and support:") + .color_range(2, ..) + )) + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new("Discord: "))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://discord.com/invite/CrUAFH3"))) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(discord_link_text_selected), + Box::new(discord_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://discord.com/invite/CrUAFH3".to_owned(), + link_executable.clone(), + )), + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new("Matrix: "))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://matrix.to/#/#zellij_general:matrix.org"))) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(matrix_link_text_selected), + Box::new(matrix_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://matrix.to/#/#zellij_general:matrix.org".to_owned(), + link_executable.clone(), + )), + ]) + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_10(link_executable: Rc>, base_mode: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #10").color_range(0, ..)) + .with_bulletin_list( + BulletinList::new( + Text::new("The Zellij session-manager can:").color_range(2, 11..=25), + ) + .with_items(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new( + "Create new sessions", + ))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new( + "Switch between existing sessions", + ))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new( + "Resurrect exited sessions", + ))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new( + "Change the session name", + ))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new( + "Disconnect other users from the current session", + ))), + ]), + ) + .with_paragraph(vec![ComponentLine::new(vec![ActiveComponent::new( + TextOrCustomRender::Text(match *base_mode.borrow() { + InputMode::Locked => Text::new("Check it out with with: Ctrl g + o + w") + .color_range(3, 24..=29) + .color_indices(3, vec![33, 37]), + _ => Text::new("Check it out with with: Ctrl o + w") + .color_range(3, 24..=29) + .color_indices(3, vec![33]), + }), + )])]) + .with_paragraph(vec![ComponentLine::new(vec![ActiveComponent::new( + TextOrCustomRender::Text( + Text::new("You can also use it as a welcome screen with: zellij -l welcome") + .color_range(0, 46..=62), + ), + )])]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_11(link_executable: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #11").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("You can change the arrangement of panes on screen with Alt + []") + .color_range(0, 55..=57) + .color_range(2, 61..=62) + )), + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("This works with tiled or floating panes, depending which is visible.") + )) + ]) + ]) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Resizing or splitting a pane breaks out of this arrangement. It is then possible") + )), + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("to snap back by pressing Alt + [] once more. This status can be seen") + .color_range(0, 25..=27) + .color_range(2, 31..=32) + )), + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("on the top right corner of the screen.") + )), + ]), + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } + pub fn tip_12(link_executable: Rc>, base_mode: Rc>) -> Page { + Page::new() + .main_screen() + .with_title(Text::new("Zellij Tip #12").color_range(0, ..)) + .with_paragraph(vec![ + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + Text::new("Zellij plugins can be loaded, reloaded and tracked from the plugin-manager.") + )), + ]), + ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text( + match *base_mode.borrow() { + InputMode::Locked => { + Text::new("Check it out with with: Ctrl g + o + p") + .color_range(3, 24..=29) + .color_indices(3, vec![33, 37]) + }, + _ => { + Text::new("Check it out with with: Ctrl o + p") + .color_range(3, 24..=29) + .color_indices(3, vec![33]) + } + } + )), + ]), + ]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(Text::new("To learn more about plugins: ").color_range(2, ..))), + ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://zellij.dev/documentation/plugins"))) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(plugin_docs_link_text_selected), + Box::new(plugin_docs_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://zellij.dev/documentation/plugins".to_owned(), + link_executable.clone(), + )), + ])]) + .with_paragraph(vec![ComponentLine::new(vec![ + ActiveComponent::new(TextOrCustomRender::Text(support_the_developer_text())), + ActiveComponent::new(TextOrCustomRender::Text(sponsors_link_text_unselected())) + .with_hover(TextOrCustomRender::CustomRender( + Box::new(sponsors_link_text_selected), + Box::new(sponsors_link_text_selected_len), + )) + .with_left_click_action(ClickAction::new_open_link( + "https://github.com/sponsors/imsnif".to_owned(), + link_executable.clone(), + )), + ])]) + .with_help(Box::new(|hovering_over_link, _menu_item_is_selected| { + tips_help_text(hovering_over_link) + })) + } +} + +fn sponsors_link_text_unselected() -> Text { + Text::new("https://github.com/sponsors/imsnif") +} + +fn sponsors_link_text_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://github.com/sponsors/imsnif", + y + 1, + x + 1 + ); + 34 +} + +fn sponsors_link_text_selected_len() -> usize { + 34 +} + +fn plugin_docs_link_text_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/documentation/plugins", + y + 1, + x + 1 + ); + 40 +} + +fn plugin_docs_link_text_selected_len() -> usize { + 40 +} + +fn awesome_zellij_link_text_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://github.com/zellij-org/awesome-zellij", + y + 1, + x + 1 + ); + 44 +} + +fn awesome_zellij_link_text_selected_len() -> usize { + 44 +} + +fn discord_link_text_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://discord.com/invite/CrUAFH3", + y + 1, + x + 1 + ); + 34 +} + +fn discord_link_text_selected_len() -> usize { + 34 +} + +fn matrix_link_text_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://matrix.to/#/#zellij_general:matrix.org", + y + 1, + x + 1 + ); + 46 +} + +fn matrix_link_text_selected_len() -> usize { + 46 +} + +fn stacked_resize_screencast_link_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/screencasts/stacked-resize", + y + 1, + x + 1 + ); + 45 +} + +fn stacked_resize_screencast_link_selected_len() -> usize { + 45 +} + +fn colliding_keybindings_link_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/tutorials/colliding-keybindings", + y + 1, + x + 1 + ); + 51 +} + +fn colliding_keybindings_link_selected_len() -> usize { + 51 +} + +fn theme_link_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/documentation/themes", + y + 1, + x + 1 + ); + 39 +} +fn theme_link_selected_len() -> usize { + 39 +} + +fn theme_list_selected(x: usize, y: usize) -> usize { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/documentation/theme-list", + y + 1, + x + 1 + ); + 43 +} +fn theme_list_selected_len() -> usize { + 43 +} + +fn support_the_developer_text() -> Text { + let support_text = format!("Please support the Zellij developer <3: "); + Text::new(support_text).color_range(3, ..) +} + +fn screencasts_link_selected() -> Box usize> { + Box::new(move |x, y| { + print!( + "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/screencasts", + y + 1, + x + 1, + ); + 30 + }) +} + +fn screencasts_link_selected_len() -> Box usize> { + Box::new(move || 30) +} + +fn tips_help_text(hovering_over_link: bool) -> Text { + if hovering_over_link { + let help_text = format!("Help: Click or Shift-Click to open in browser"); + Text::new(help_text) + .color_range(3, 6..=10) + .color_range(3, 15..=25) + } else { + let help_text = format!( + "Help: - Dismiss, <↓↑> - Browse tips, - Don't show tips on startup" + ); + Text::new(help_text) + .color_range(1, 6..=10) + .color_range(1, 23..=26) + .color_range(1, 43..=50) + } +} diff --git a/default-plugins/configuration/src/presets.rs b/default-plugins/configuration/src/presets.rs index eb9a55bb..41d7a79e 100644 --- a/default-plugins/configuration/src/presets.rs +++ b/default-plugins/configuration/src/presets.rs @@ -140,6 +140,13 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Locked" }} + bind "a" {{ + LaunchOrFocusPlugin "zellij:about" {{ + floating true + move_to_focused_tab true + }}; + SwitchToMode "Locked" + }} }} shared_except "locked" "renametab" "renamepane" {{ bind "{primary_modifier} g" {{ SwitchToMode "Locked"; }} @@ -339,6 +346,13 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} + bind "a" {{ + LaunchOrFocusPlugin "zellij:about" {{ + floating true + move_to_focused_tab true + }}; + SwitchToMode "Normal" + }} }} tmux {{ bind "[" {{ SwitchToMode "Scroll"; }} @@ -538,6 +552,13 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} + bind "a" {{ + LaunchOrFocusPlugin "zellij:about" {{ + floating true + move_to_focused_tab true + }}; + SwitchToMode "Normal" + }} }} tmux {{ bind "[" {{ SwitchToMode "Scroll"; }} @@ -718,6 +739,13 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} + bind "a" {{ + LaunchOrFocusPlugin "zellij:about" {{ + floating true + move_to_focused_tab true + }}; + SwitchToMode "Normal" + }} }} tmux {{ bind "[" {{ SwitchToMode "Scroll"; }} @@ -900,6 +928,13 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} + bind "a" {{ + LaunchOrFocusPlugin "zellij:about" {{ + floating true + move_to_focused_tab true + }}; + SwitchToMode "Normal" + }} }} tmux {{ bind "[" {{ SwitchToMode "Scroll"; }} @@ -1077,6 +1112,13 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} + bind "a" {{ + LaunchOrFocusPlugin "zellij:about" {{ + floating true + move_to_focused_tab true + }}; + SwitchToMode "Normal" + }} }} tmux {{ bind "[" {{ SwitchToMode "Scroll"; }} diff --git a/default-plugins/status-bar/src/one_line_ui.rs b/default-plugins/status-bar/src/one_line_ui.rs index 4d47281e..3b7eec10 100644 --- a/default-plugins/status-bar/src/one_line_ui.rs +++ b/default-plugins/status-bar/src/one_line_ui.rs @@ -1276,6 +1276,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)]) -> Vec)]) -> Vec { + let mut matching = keymap.iter().find_map(|(key, acvec)| { + let has_match = acvec + .iter() + .find(|a| a.launches_plugin("zellij:about")) + .is_some(); + if has_match { + Some(key.clone()) + } else { + None + } + }); + if let Some(matching) = matching.take() { + vec![matching] + } else { + vec![] + } +} + fn configuration_key(keymap: &[(KeyWithModifier, Vec)]) -> Vec { let mut matching = keymap.iter().find_map(|(key, acvec)| { let has_match = acvec diff --git a/example/default.kdl b/example/default.kdl index 2a4b3f9c..e04601b1 100644 --- a/example/default.kdl +++ b/example/default.kdl @@ -135,6 +135,13 @@ keybinds { }; SwitchToMode "Normal" } + bind "a" { + LaunchOrFocusPlugin "zellij:about" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Normal" + } } tmux { bind "[" { SwitchToMode "Scroll"; } @@ -413,3 +420,13 @@ load_plugins { // Default: true // // stacked_resize false + +// Whether to show tips on startup +// Default: true +// +// show_tips_on_startup false + +// Whether to show release notes on first version run +// Default: true +// +// show_release_notes false diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index 1c05f502..b3d8b9ac 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -81,7 +81,7 @@ fn start_zellij(channel: &mut ssh2::Channel) { channel .write_all( format!( - "{} {} --session {} --data-dir {}\n", + "{} {} --session {} --data-dir {} options --show-release-notes false --show-startup-tips false\n", SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR ) .as_bytes(), @@ -96,7 +96,7 @@ fn start_zellij_mirrored_session(channel: &mut ssh2::Channel) { channel .write_all( format!( - "{} {} --session {} --data-dir {} options --mirror-session true --serialization-interval 1\n", + "{} {} --session {} --data-dir {} options --show-release-notes false --show-startup-tips false --mirror-session true --serialization-interval 1\n", SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR ) .as_bytes(), @@ -111,7 +111,7 @@ fn start_zellij_mirrored_session_with_layout(channel: &mut ssh2::Channel, layout channel .write_all( format!( - "{} {} --session {} --data-dir {} --new-session-with-layout {} options --mirror-session true --serialization-interval 1\n", + "{} {} --session {} --data-dir {} --new-session-with-layout {} options --show-release-notes false --show-startup-tips false --mirror-session true --serialization-interval 1\n", SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, @@ -133,7 +133,7 @@ fn start_zellij_mirrored_session_with_layout_and_viewport_serialization( channel .write_all( format!( - "{} {} --session {} --data-dir {} --new-session-with-layout {} options --mirror-session true --serialize-pane-viewport true --serialization-interval 1\n", + "{} {} --session {} --data-dir {} --new-session-with-layout {} options --show-release-notes false --show-startup-tips false --mirror-session true --serialize-pane-viewport true --serialization-interval 1\n", SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, @@ -152,7 +152,7 @@ fn start_zellij_in_session(channel: &mut ssh2::Channel, session_name: &str, mirr channel .write_all( format!( - "{} {} --session {} --data-dir {} options --mirror-session {}\n", + "{} {} --session {} --data-dir {} options --show-release-notes false --show-startup-tips false --mirror-session {}\n", SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, session_name, @@ -185,7 +185,7 @@ fn start_zellij_without_frames(channel: &mut ssh2::Channel) { channel .write_all( format!( - "{} {} --session {} --data-dir {} options --no-pane-frames\n", + "{} {} --session {} --data-dir {} options --show-release-notes false --show-startup-tips false --no-pane-frames\n", SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR ) .as_bytes(), @@ -200,7 +200,7 @@ fn start_zellij_with_config(channel: &mut ssh2::Channel, config_path: &str) { channel .write_all( format!( - "{} {} --config {} --session {} --data-dir {}\n", + "{} {} --config {} --session {} --data-dir {} options --show-release-notes false --show-startup-tips false\n", SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, config_path, diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 0e05fde5..879fd545 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -336,7 +336,11 @@ impl SessionMetaData { self.current_input_modes.insert(client_id, input_mode); } } - pub fn propagate_configuration_changes(&mut self, config_changes: Vec<(ClientId, Config)>) { + pub fn propagate_configuration_changes( + &mut self, + config_changes: Vec<(ClientId, Config)>, + config_was_written_to_disk: bool, + ) { for (client_id, new_config) in config_changes { self.default_shell = new_config.options.default_shell.as_ref().map(|shell| { TerminalAction::RunCommand(RunCommand { @@ -375,6 +379,7 @@ impl SessionMetaData { keybinds: Some(new_config.keybinds), default_mode: new_config.options.default_mode, default_shell: self.default_shell.clone(), + was_written_to_disk: config_was_written_to_disk, }) .unwrap(); self.senders @@ -706,9 +711,12 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { // intrusive let setup_wizard = setup_wizard_floating_pane(); floating_panes.push(setup_wizard); - } else if should_show_release_notes() { + } else if should_show_release_notes(runtime_config_options.show_release_notes) { let about = about_floating_pane(); floating_panes.push(about); + } else if should_show_startup_tip(runtime_config_options.show_startup_tips) { + let tip = tip_floating_pane(); + floating_panes.push(tip); } spawn_tabs( None, @@ -1166,12 +1174,16 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { } if runtime_config_changed { + let config_was_written_to_disk = false; session_data .write() .unwrap() .as_mut() .unwrap() - .propagate_configuration_changes(vec![(client_id, new_config)]); + .propagate_configuration_changes( + vec![(client_id, new_config)], + config_was_written_to_disk, + ); } } }, @@ -1183,12 +1195,13 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { .unwrap() .session_configuration .new_saved_config(client_id, new_config); + let config_was_written_to_disk = true; session_data .write() .unwrap() .as_mut() .unwrap() - .propagate_configuration_changes(changes); + .propagate_configuration_changes(changes, config_was_written_to_disk); }, ServerInstruction::FailedToWriteConfigToDisk(_client_id, file_path) => { session_data @@ -1227,12 +1240,16 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { } if runtime_config_changed { + let config_was_written_to_disk = false; session_data .write() .unwrap() .as_mut() .unwrap() - .propagate_configuration_changes(vec![(client_id, new_config)]); + .propagate_configuration_changes( + vec![(client_id, new_config)], + config_was_written_to_disk, + ); } } }, @@ -1506,7 +1523,26 @@ fn about_floating_pane() -> FloatingPaneLayout { about_pane } -fn should_show_release_notes() -> bool { +fn tip_floating_pane() -> FloatingPaneLayout { + let mut about_pane = FloatingPaneLayout::new(); + let configuration = BTreeMap::from_iter([("is_startup_tip".to_owned(), "true".to_owned())]); + about_pane.run = Some(Run::Plugin(RunPluginOrAlias::Alias(PluginAlias::new( + "about", + &Some(configuration), + None, + )))); + about_pane +} + +fn should_show_release_notes(should_show_release_notes_config: Option) -> bool { + if let Some(should_show_release_notes_config) = should_show_release_notes_config { + if !should_show_release_notes_config { + // if we were explicitly told not to show release notes, we don't show them, + // otherwise we make sure we only show them if they were not seen AND we know + // we are able to write to the cache + return false; + } + } if ZELLIJ_SEEN_RELEASE_NOTES_CACHE_FILE.exists() { return false; } else { @@ -1521,6 +1557,10 @@ fn should_show_release_notes() -> bool { } } +fn should_show_startup_tip(should_show_startup_tip_config: Option) -> bool { + should_show_startup_tip_config.unwrap_or(true) +} + #[cfg(not(feature = "singlepass"))] fn get_engine() -> Engine { log::info!("Compiling plugins using Cranelift"); diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs index fa1fabce..f1d11377 100644 --- a/zellij-server/src/plugins/mod.rs +++ b/zellij-server/src/plugins/mod.rs @@ -152,6 +152,7 @@ pub enum PluginInstruction { keybinds: Option, default_mode: Option, default_shell: Option, + was_written_to_disk: bool, }, FailedToWriteConfigToDisk { file_path: Option, @@ -868,12 +869,19 @@ pub(crate) fn plugin_thread_main( keybinds, default_mode, default_shell, + was_written_to_disk, } => { - // TODO: notify plugins that this happened so that they can eg. rebind temporary keys that - // were lost wasm_bridge .reconfigure(client_id, keybinds, default_mode, default_shell) .non_fatal(); + // TODO: notify plugins that this happened so that they can eg. rebind temporary keys that + // were lost + if was_written_to_disk { + let updates = vec![(None, None, Event::ConfigWasWrittenToDisk)]; + wasm_bridge + .update_plugins(updates, shutdown_send.clone()) + .non_fatal(); + } }, PluginInstruction::FailedToWriteConfigToDisk { file_path } => { let updates = vec![( diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 616e2452..298127b0 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -135,6 +135,13 @@ keybinds { }; SwitchToMode "Normal" } + bind "a" { + LaunchOrFocusPlugin "zellij:about" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Normal" + } } tmux { bind "[" { SwitchToMode "Scroll"; } @@ -414,3 +421,13 @@ load_plugins { // Default: true // // stacked_resize false + +// Whether to show release notes on first version run +// Default: true +// +// show_release_notes false + +// Whether to show startup tips on session start +// Default: true +// +// show_startup_tips false diff --git a/zellij-utils/assets/plugins/about.wasm b/zellij-utils/assets/plugins/about.wasm index b7fa9e77..e1bb7aae 100755 Binary files a/zellij-utils/assets/plugins/about.wasm and b/zellij-utils/assets/plugins/about.wasm differ diff --git a/zellij-utils/assets/plugins/compact-bar.wasm b/zellij-utils/assets/plugins/compact-bar.wasm index e3d2506e..a0d40d9f 100755 Binary files a/zellij-utils/assets/plugins/compact-bar.wasm and b/zellij-utils/assets/plugins/compact-bar.wasm differ diff --git a/zellij-utils/assets/plugins/configuration.wasm b/zellij-utils/assets/plugins/configuration.wasm index e741588e..51a029aa 100755 Binary files a/zellij-utils/assets/plugins/configuration.wasm and b/zellij-utils/assets/plugins/configuration.wasm differ diff --git a/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm index 507416c9..991a74fc 100755 Binary files a/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm and b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm differ diff --git a/zellij-utils/assets/plugins/plugin-manager.wasm b/zellij-utils/assets/plugins/plugin-manager.wasm index 546c8700..10d05d9b 100755 Binary files a/zellij-utils/assets/plugins/plugin-manager.wasm and b/zellij-utils/assets/plugins/plugin-manager.wasm differ diff --git a/zellij-utils/assets/plugins/session-manager.wasm b/zellij-utils/assets/plugins/session-manager.wasm index 2f5406e0..57dce9b6 100755 Binary files a/zellij-utils/assets/plugins/session-manager.wasm and b/zellij-utils/assets/plugins/session-manager.wasm differ diff --git a/zellij-utils/assets/plugins/status-bar.wasm b/zellij-utils/assets/plugins/status-bar.wasm index 676e74ec..cc7668f5 100755 Binary files a/zellij-utils/assets/plugins/status-bar.wasm and b/zellij-utils/assets/plugins/status-bar.wasm differ diff --git a/zellij-utils/assets/plugins/strider.wasm b/zellij-utils/assets/plugins/strider.wasm index 0e7632b4..15123089 100755 Binary files a/zellij-utils/assets/plugins/strider.wasm and b/zellij-utils/assets/plugins/strider.wasm differ diff --git a/zellij-utils/assets/plugins/tab-bar.wasm b/zellij-utils/assets/plugins/tab-bar.wasm index b1f21cb5..db09ceab 100755 Binary files a/zellij-utils/assets/plugins/tab-bar.wasm and b/zellij-utils/assets/plugins/tab-bar.wasm differ diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs index 3378cec9..85db2531 100644 --- a/zellij-utils/assets/prost/api.event.rs +++ b/zellij-utils/assets/prost/api.event.rs @@ -507,6 +507,7 @@ pub enum EventType { HostFolderChanged = 27, FailedToChangeHostFolder = 28, PastedText = 29, + ConfigWasWrittenToDisk = 30, } impl EventType { /// String value of the enum field names used in the ProtoBuf definition. @@ -545,6 +546,7 @@ impl EventType { EventType::HostFolderChanged => "HostFolderChanged", EventType::FailedToChangeHostFolder => "FailedToChangeHostFolder", EventType::PastedText => "PastedText", + EventType::ConfigWasWrittenToDisk => "ConfigWasWrittenToDisk", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -580,6 +582,7 @@ impl EventType { "HostFolderChanged" => Some(Self::HostFolderChanged), "FailedToChangeHostFolder" => Some(Self::FailedToChangeHostFolder), "PastedText" => Some(Self::PastedText), + "ConfigWasWrittenToDisk" => Some(Self::ConfigWasWrittenToDisk), _ => None, } } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index bbbc47df..5bc1732d 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -932,6 +932,7 @@ pub enum Event { HostFolderChanged(PathBuf), // PathBuf -> new host folder FailedToChangeHostFolder(Option), // String -> the error we got when changing PastedText(String), + ConfigWasWrittenToDisk, } #[derive( diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index 5ece3c58..a9f82f02 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -167,6 +167,18 @@ pub struct Options { #[clap(long, value_parser)] #[serde(default)] pub stacked_resize: Option, + + /// Whether to show startup tips when starting a new session + /// default is true + #[clap(long, value_parser)] + #[serde(default)] + pub show_startup_tips: Option, + + /// Whether to show release notes on first run of a new version + /// default is true + #[clap(long, value_parser)] + #[serde(default)] + pub show_release_notes: Option, } #[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] @@ -246,6 +258,8 @@ impl Options { .support_kitty_keyboard_protocol .or(self.support_kitty_keyboard_protocol); let stacked_resize = other.stacked_resize.or(self.stacked_resize); + let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips); + let show_release_notes = other.show_release_notes.or(self.show_release_notes); Options { simplified_ui, @@ -276,6 +290,8 @@ impl Options { disable_session_metadata, support_kitty_keyboard_protocol, stacked_resize, + show_startup_tips, + show_release_notes, } } @@ -335,6 +351,8 @@ impl Options { .support_kitty_keyboard_protocol .or(self.support_kitty_keyboard_protocol); let stacked_resize = other.stacked_resize.or(self.stacked_resize); + let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips); + let show_release_notes = other.show_release_notes.or(self.show_release_notes); Options { simplified_ui, @@ -365,6 +383,8 @@ impl Options { disable_session_metadata, support_kitty_keyboard_protocol, stacked_resize, + show_startup_tips, + show_release_notes, } } @@ -431,6 +451,8 @@ impl From for Options { serialization_interval: opts.serialization_interval, support_kitty_keyboard_protocol: opts.support_kitty_keyboard_protocol, stacked_resize: opts.stacked_resize, + show_release_notes: opts.show_release_notes, + show_startup_tips: opts.show_startup_tips, ..Default::default() } } diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index ad4c86db..eeabb520 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -2293,6 +2293,12 @@ impl Options { .map(|(v, _)| v); let stacked_resize = kdl_property_first_arg_as_bool_or_error!(kdl_options, "stacked_resize").map(|(v, _)| v); + let show_startup_tips = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "show_startup_tips") + .map(|(v, _)| v); + let show_release_notes = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "show_release_notes") + .map(|(v, _)| v); Ok(Options { simplified_ui, theme, @@ -2322,6 +2328,8 @@ impl Options { disable_session_metadata, support_kitty_keyboard_protocol, stacked_resize, + show_startup_tips, + show_release_notes, }) } pub fn from_string(stringified_keybindings: &String) -> Result { @@ -3149,6 +3157,56 @@ impl Options { None } } + fn show_startup_tips_to_kdl(&self, add_comments: bool) -> Option { + let comment_text = format!( + "{}\n{}\n{}\n{}", + " ", "// Whether to show tips on startup", "// Default: true", "// ", + ); + + let create_node = |node_value: bool| -> KdlNode { + let mut node = KdlNode::new("show_startup_tips"); + node.push(KdlValue::Bool(node_value)); + node + }; + if let Some(show_startup_tips) = self.show_startup_tips { + let mut node = create_node(show_startup_tips); + if add_comments { + node.set_leading(format!("{}\n", comment_text)); + } + Some(node) + } else if add_comments { + let mut node = create_node(false); + node.set_leading(format!("{}\n// ", comment_text)); + Some(node) + } else { + None + } + } + fn show_release_notes_to_kdl(&self, add_comments: bool) -> Option { + let comment_text = format!( + "{}\n{}\n{}\n{}", + " ", "// Whether to show release notes on first version run", "// Default: true", "// ", + ); + + let create_node = |node_value: bool| -> KdlNode { + let mut node = KdlNode::new("show_release_notes"); + node.push(KdlValue::Bool(node_value)); + node + }; + if let Some(show_release_notes) = self.show_release_notes { + let mut node = create_node(show_release_notes); + if add_comments { + node.set_leading(format!("{}\n", comment_text)); + } + Some(node) + } else if add_comments { + let mut node = create_node(false); + node.set_leading(format!("{}\n// ", comment_text)); + Some(node) + } else { + None + } + } pub fn to_kdl(&self, add_comments: bool) -> Vec { let mut nodes = vec![]; if let Some(simplified_ui_node) = self.simplified_ui_to_kdl(add_comments) { @@ -3239,6 +3297,12 @@ impl Options { if let Some(stacked_resize) = self.stacked_resize_to_kdl(add_comments) { nodes.push(stacked_resize); } + if let Some(show_startup_tips) = self.show_startup_tips_to_kdl(add_comments) { + nodes.push(show_startup_tips); + } + if let Some(show_release_notes) = self.show_release_notes_to_kdl(add_comments) { + nodes.push(show_release_notes); + } nodes } } diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap index 9a2030da..c5e53358 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5626 +assertion_line: 5922 expression: fake_config_stringified --- keybinds clear-defaults=true { @@ -100,6 +100,13 @@ keybinds clear-defaults=true { bind "w" { SearchToggleOption "Wrap"; } } session { + bind "a" { + LaunchOrFocusPlugin "zellij:about" { + floating true + move_to_focused_tab true + } + SwitchToMode "normal" + } bind "c" { LaunchOrFocusPlugin "configuration" { floating true diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap index 7c38a7e6..5ace8251 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5638 +assertion_line: 5934 expression: fake_config_stringified --- keybinds clear-defaults=true { @@ -100,6 +100,13 @@ keybinds clear-defaults=true { bind "w" { SearchToggleOption "Wrap"; } } session { + bind "a" { + LaunchOrFocusPlugin "zellij:about" { + floating true + move_to_focused_tab true + } + SwitchToMode "normal" + } bind "c" { LaunchOrFocusPlugin "configuration" { floating true @@ -438,4 +445,14 @@ load_plugins { // Default: true // // stacked_resize false + +// Whether to show tips on startup +// Default: true +// +// show_startup_tips false + +// Whether to show release notes on first version run +// Default: true +// +// show_release_notes false diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap index 61361362..e3c4368e 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5537 +assertion_line: 5873 expression: fake_document.to_string() --- @@ -185,4 +185,14 @@ support_kitty_keyboard_protocol false // Default: true // // stacked_resize false + +// Whether to show tips on startup +// Default: true +// +// show_startup_tips false + +// Whether to show release notes on first version run +// Default: true +// +// show_release_notes false diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index f6162b68..b07562e2 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -53,6 +53,7 @@ enum EventType { HostFolderChanged = 27; FailedToChangeHostFolder = 28; PastedText = 29; + ConfigWasWrittenToDisk = 30; } message EventNameList { diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 0f570aee..495060a6 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -355,6 +355,10 @@ impl TryFrom for Event { }, _ => Err("Malformed payload for the PastedText Event"), }, + Some(ProtobufEventType::ConfigWasWrittenToDisk) => match protobuf_event.payload { + None => Ok(Event::ConfigWasWrittenToDisk), + _ => Err("Malformed payload for the ConfigWasWrittenToDisk Event"), + }, None => Err("Unknown Protobuf Event"), } } @@ -725,6 +729,10 @@ impl TryFrom for ProtobufEvent { pasted_text, })), }), + Event::ConfigWasWrittenToDisk => Ok(ProtobufEvent { + name: ProtobufEventType::ConfigWasWrittenToDisk as i32, + payload: None, + }), } } } @@ -1306,6 +1314,7 @@ impl TryFrom for EventType { ProtobufEventType::HostFolderChanged => EventType::HostFolderChanged, ProtobufEventType::FailedToChangeHostFolder => EventType::FailedToChangeHostFolder, ProtobufEventType::PastedText => EventType::PastedText, + ProtobufEventType::ConfigWasWrittenToDisk => EventType::ConfigWasWrittenToDisk, }) } } @@ -1344,6 +1353,7 @@ impl TryFrom for ProtobufEventType { EventType::HostFolderChanged => ProtobufEventType::HostFolderChanged, EventType::FailedToChangeHostFolder => ProtobufEventType::FailedToChangeHostFolder, EventType::PastedText => ProtobufEventType::PastedText, + EventType::ConfigWasWrittenToDisk => ProtobufEventType::ConfigWasWrittenToDisk, }) } } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap index 4d2c2fee..51f0eff1 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap @@ -34,4 +34,6 @@ Options { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap index db16d8ce..ca547f2d 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap @@ -34,4 +34,6 @@ Options { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap index 621bc4a7..79043ec6 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap @@ -32,4 +32,6 @@ Options { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 11880e19..7fd6ab84 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -3956,6 +3956,36 @@ Config { }: [ NextSwapLayout, ], + KeyWithModifier { + bare_key: Char( + 'a', + ), + key_modifiers: {}, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "about", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + SwitchToMode( + Normal, + ), + ], KeyWithModifier { bare_key: Char( 'b', @@ -5611,6 +5641,8 @@ Config { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 3c486c5b..cc836b3e 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -3956,6 +3956,36 @@ Config { }: [ NextSwapLayout, ], + KeyWithModifier { + bare_key: Char( + 'a', + ), + key_modifiers: {}, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "about", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + SwitchToMode( + Normal, + ), + ], KeyWithModifier { bare_key: Char( 'b', @@ -5611,6 +5641,8 @@ Config { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap index 7bd4a866..c13ecea9 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap @@ -119,6 +119,8 @@ Config { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap index 46d265d1..59a8e93a 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap @@ -34,4 +34,6 @@ Options { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index d94b71c6..c70aff9b 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -1,5 +1,6 @@ --- source: zellij-utils/src/setup.rs +assertion_line: 841 expression: "format!(\"{:#?}\", config)" --- Config { @@ -3955,6 +3956,36 @@ Config { }: [ NextSwapLayout, ], + KeyWithModifier { + bare_key: Char( + 'a', + ), + key_modifiers: {}, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "about", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + SwitchToMode( + Normal, + ), + ], KeyWithModifier { bare_key: Char( 'b', @@ -5610,6 +5641,8 @@ Config { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, }, themes: { "other-theme-from-config": Theme { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index fb4b78bd..be3ec13f 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -3956,6 +3956,36 @@ Config { }: [ NextSwapLayout, ], + KeyWithModifier { + bare_key: Char( + 'a', + ), + key_modifiers: {}, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "about", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + SwitchToMode( + Normal, + ), + ], KeyWithModifier { bare_key: Char( 'b', @@ -5611,6 +5641,8 @@ Config { disable_session_metadata: None, support_kitty_keyboard_protocol: None, stacked_resize: None, + show_startup_tips: None, + show_release_notes: None, }, themes: {}, plugins: PluginAliases {