mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-22 05:12:27 +00:00
Compare commits
5 Commits
c4a661ab4e
...
b9ebbba662
Author | SHA1 | Date | |
---|---|---|---|
|
b9ebbba662 | ||
|
696110eae5 | ||
|
fa8bb74527 | ||
|
3796c7abdb | ||
|
8edd12f0b4 |
19
.github/release.yml
vendored
19
.github/release.yml
vendored
|
@ -1,20 +1,23 @@
|
||||||
changelog:
|
changelog:
|
||||||
categories:
|
categories:
|
||||||
- title: '🚀 Features'
|
- title: '🚀 Features'
|
||||||
labels:
|
label: 'enhancement'
|
||||||
- 'feature'
|
|
||||||
- 'enhancement'
|
|
||||||
- title: '🐛 Bug Fixes'
|
- title: '🐛 Bug Fixes'
|
||||||
labels:
|
label: 'bug'
|
||||||
- 'fix'
|
- title: '⚙️ Refactoring'
|
||||||
- 'bugfix'
|
label: 'refactor'
|
||||||
- 'bug'
|
- title: '🧩 UI/UX'
|
||||||
|
label: 'UI/UX'
|
||||||
- title: '📚 Documentation'
|
- title: '📚 Documentation'
|
||||||
label: 'documentation'
|
label: 'documentation'
|
||||||
- title: '🔒 Security'
|
- title: '🔒 Security'
|
||||||
label: 'security'
|
label: 'security'
|
||||||
- title: '🧰 GitHub Actions'
|
- title: '🧰 GitHub Actions'
|
||||||
label: 'github actions'
|
label: 'github_actions'
|
||||||
|
- title: '🦀 Rust'
|
||||||
|
label: 'rust'
|
||||||
|
- title: '📃 Scripting'
|
||||||
|
label: 'script'
|
||||||
exclude:
|
exclude:
|
||||||
labels:
|
labels:
|
||||||
- 'skip-changelog'
|
- 'skip-changelog'
|
|
@ -33,30 +33,18 @@ pub fn get_tabs(validate: bool) -> (TempDir, Vec<Tab>) {
|
||||||
|
|
||||||
let tabs: Vec<Tab> = tabs
|
let tabs: Vec<Tab> = tabs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(
|
.map(|(TabEntry { name, data }, directory)| {
|
||||||
|(
|
let mut tree = Tree::new(Rc::new(ListNode {
|
||||||
TabEntry {
|
name: "root".to_string(),
|
||||||
name,
|
description: String::new(),
|
||||||
data,
|
command: Command::None,
|
||||||
multi_selectable,
|
task_list: String::new(),
|
||||||
},
|
multi_select: false,
|
||||||
directory,
|
}));
|
||||||
)| {
|
let mut root = tree.root_mut();
|
||||||
let mut tree = Tree::new(Rc::new(ListNode {
|
create_directory(data, &mut root, &directory, validate, true);
|
||||||
name: "root".to_string(),
|
Tab { name, tree }
|
||||||
description: String::new(),
|
})
|
||||||
command: Command::None,
|
|
||||||
task_list: String::new(),
|
|
||||||
}));
|
|
||||||
let mut root = tree.root_mut();
|
|
||||||
create_directory(data, &mut root, &directory, validate);
|
|
||||||
Tab {
|
|
||||||
name,
|
|
||||||
tree,
|
|
||||||
multi_selectable,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if tabs.is_empty() {
|
if tabs.is_empty() {
|
||||||
|
@ -74,12 +62,6 @@ struct TabList {
|
||||||
struct TabEntry {
|
struct TabEntry {
|
||||||
name: String,
|
name: String,
|
||||||
data: Vec<Entry>,
|
data: Vec<Entry>,
|
||||||
#[serde(default = "default_multi_selectable")]
|
|
||||||
multi_selectable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_multi_selectable() -> bool {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -94,6 +76,12 @@ struct Entry {
|
||||||
entry_type: EntryType,
|
entry_type: EntryType,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
task_list: String,
|
task_list: String,
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
multi_select: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_true() -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -174,8 +162,11 @@ fn create_directory(
|
||||||
node: &mut NodeMut<Rc<ListNode>>,
|
node: &mut NodeMut<Rc<ListNode>>,
|
||||||
command_dir: &Path,
|
command_dir: &Path,
|
||||||
validate: bool,
|
validate: bool,
|
||||||
|
parent_multi_select: bool,
|
||||||
) {
|
) {
|
||||||
for entry in data {
|
for entry in data {
|
||||||
|
let multi_select = parent_multi_select && entry.multi_select;
|
||||||
|
|
||||||
match entry.entry_type {
|
match entry.entry_type {
|
||||||
EntryType::Entries(entries) => {
|
EntryType::Entries(entries) => {
|
||||||
let mut node = node.append(Rc::new(ListNode {
|
let mut node = node.append(Rc::new(ListNode {
|
||||||
|
@ -183,8 +174,9 @@ fn create_directory(
|
||||||
description: entry.description,
|
description: entry.description,
|
||||||
command: Command::None,
|
command: Command::None,
|
||||||
task_list: String::new(),
|
task_list: String::new(),
|
||||||
|
multi_select,
|
||||||
}));
|
}));
|
||||||
create_directory(entries, &mut node, command_dir, validate);
|
create_directory(entries, &mut node, command_dir, validate, multi_select);
|
||||||
}
|
}
|
||||||
EntryType::Command(command) => {
|
EntryType::Command(command) => {
|
||||||
node.append(Rc::new(ListNode {
|
node.append(Rc::new(ListNode {
|
||||||
|
@ -192,6 +184,7 @@ fn create_directory(
|
||||||
description: entry.description,
|
description: entry.description,
|
||||||
command: Command::Raw(command),
|
command: Command::Raw(command),
|
||||||
task_list: String::new(),
|
task_list: String::new(),
|
||||||
|
multi_select,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
EntryType::Script(script) => {
|
EntryType::Script(script) => {
|
||||||
|
@ -210,6 +203,7 @@ fn create_directory(
|
||||||
file: script,
|
file: script,
|
||||||
},
|
},
|
||||||
task_list: entry.task_list,
|
task_list: entry.task_list,
|
||||||
|
multi_select,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ pub enum Command {
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub tree: Tree<Rc<ListNode>>,
|
pub tree: Tree<Rc<ListNode>>,
|
||||||
pub multi_selectable: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||||
|
@ -32,4 +31,5 @@ pub struct ListNode {
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub command: Command,
|
pub command: Command,
|
||||||
pub task_list: String,
|
pub task_list: String,
|
||||||
|
pub multi_select: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
name = "System Setup"
|
name = "System Setup"
|
||||||
multi_selectable = false
|
|
||||||
|
|
||||||
[[data]]
|
[[data]]
|
||||||
name = "Arch Linux"
|
name = "Arch Linux"
|
||||||
|
@ -14,6 +13,7 @@ name = "Arch Server Setup"
|
||||||
description = "This command installs a minimal arch server setup under 5 minutes."
|
description = "This command installs a minimal arch server setup under 5 minutes."
|
||||||
script = "arch/server-setup.sh"
|
script = "arch/server-setup.sh"
|
||||||
task_list = "SI D"
|
task_list = "SI D"
|
||||||
|
multi_select = false
|
||||||
|
|
||||||
[[data.entries]]
|
[[data.entries]]
|
||||||
name = "Paru AUR Helper"
|
name = "Paru AUR Helper"
|
||||||
|
@ -76,12 +76,14 @@ name = "Full System Cleanup"
|
||||||
description = "This script is designed to remove unnecessary packages, clean old cache files, remove temporary files, and to empty the trash."
|
description = "This script is designed to remove unnecessary packages, clean old cache files, remove temporary files, and to empty the trash."
|
||||||
script = "system-cleanup.sh"
|
script = "system-cleanup.sh"
|
||||||
task_list = "RP PFM"
|
task_list = "RP PFM"
|
||||||
|
multi_select = false
|
||||||
|
|
||||||
[[data]]
|
[[data]]
|
||||||
name = "Full System Update"
|
name = "Full System Update"
|
||||||
description = "This command updates your system to the latest packages available for your distro"
|
description = "This command updates your system to the latest packages available for your distro"
|
||||||
script = "system-update.sh"
|
script = "system-update.sh"
|
||||||
task_list = "PFM"
|
task_list = "PFM"
|
||||||
|
multi_select = false
|
||||||
|
|
||||||
[[data]]
|
[[data]]
|
||||||
name = "Gaming Dependencies"
|
name = "Gaming Dependencies"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
name = "Utilities"
|
name = "Utilities"
|
||||||
multi_selectable = false
|
|
||||||
|
|
||||||
[[data]]
|
[[data]]
|
||||||
name = "Monitor Control"
|
name = "Monitor Control"
|
||||||
|
multi_select = false
|
||||||
|
|
||||||
[[data.preconditions]]
|
[[data.preconditions]]
|
||||||
matches = true
|
matches = true
|
||||||
|
@ -78,6 +78,7 @@ script = "monitor-control/set_resolutions.sh"
|
||||||
|
|
||||||
[[data]]
|
[[data]]
|
||||||
name = "User Account Manager"
|
name = "User Account Manager"
|
||||||
|
multi_select = false
|
||||||
|
|
||||||
[[data.entries]]
|
[[data.entries]]
|
||||||
name = "Add User"
|
name = "Add User"
|
||||||
|
@ -104,6 +105,7 @@ name = "Auto Mount Drive"
|
||||||
description = "This utility is designed to help with automating the process of mounting a drive on to your system."
|
description = "This utility is designed to help with automating the process of mounting a drive on to your system."
|
||||||
script = "auto-mount.sh"
|
script = "auto-mount.sh"
|
||||||
task_list = "PFM"
|
task_list = "PFM"
|
||||||
|
multi_select = false
|
||||||
|
|
||||||
[[data]]
|
[[data]]
|
||||||
name = "Auto Login"
|
name = "Auto Login"
|
||||||
|
@ -120,6 +122,7 @@ name = "Bluetooth Manager"
|
||||||
description = "This utility is designed to manage bluetooth in your system"
|
description = "This utility is designed to manage bluetooth in your system"
|
||||||
script = "bluetooth-control.sh"
|
script = "bluetooth-control.sh"
|
||||||
task_list = "I SS"
|
task_list = "I SS"
|
||||||
|
multi_select = false
|
||||||
|
|
||||||
[[data]]
|
[[data]]
|
||||||
name = "Bootable USB Creator"
|
name = "Bootable USB Creator"
|
||||||
|
|
|
@ -146,12 +146,10 @@ impl AppState {
|
||||||
hints.push(Shortcut::new("Select item below", ["j", "Down"]));
|
hints.push(Shortcut::new("Select item below", ["j", "Down"]));
|
||||||
hints.push(Shortcut::new("Next theme", ["t"]));
|
hints.push(Shortcut::new("Next theme", ["t"]));
|
||||||
hints.push(Shortcut::new("Previous theme", ["T"]));
|
hints.push(Shortcut::new("Previous theme", ["T"]));
|
||||||
|
hints.push(Shortcut::new("Multi-selection mode", ["v"]));
|
||||||
if self.is_current_tab_multi_selectable() {
|
if self.multi_select {
|
||||||
hints.push(Shortcut::new("Toggle multi-selection mode", ["v"]));
|
|
||||||
hints.push(Shortcut::new("Select multiple commands", ["Space"]));
|
hints.push(Shortcut::new("Select multiple commands", ["Space"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
hints.push(Shortcut::new("Next tab", ["Tab"]));
|
hints.push(Shortcut::new("Next tab", ["Tab"]));
|
||||||
hints.push(Shortcut::new("Previous tab", ["Shift-Tab"]));
|
hints.push(Shortcut::new("Previous tab", ["Shift-Tab"]));
|
||||||
hints.push(Shortcut::new("Important actions guide", ["g"]));
|
hints.push(Shortcut::new("Important actions guide", ["g"]));
|
||||||
|
@ -323,7 +321,12 @@ impl AppState {
|
||||||
let (indicator, style) = if is_selected {
|
let (indicator, style) = if is_selected {
|
||||||
(self.theme.multi_select_icon(), Style::default().bold())
|
(self.theme.multi_select_icon(), Style::default().bold())
|
||||||
} else {
|
} else {
|
||||||
("", Style::new())
|
let ms_style = if self.multi_select && !node.multi_select {
|
||||||
|
Style::default().fg(self.theme.multi_select_disabled_color())
|
||||||
|
} else {
|
||||||
|
Style::new()
|
||||||
|
};
|
||||||
|
("", ms_style)
|
||||||
};
|
};
|
||||||
if *has_children {
|
if *has_children {
|
||||||
Line::from(format!(
|
Line::from(format!(
|
||||||
|
@ -333,6 +336,7 @@ impl AppState {
|
||||||
indicator
|
indicator
|
||||||
))
|
))
|
||||||
.style(self.theme.dir_color())
|
.style(self.theme.dir_color())
|
||||||
|
.patch_style(style)
|
||||||
} else {
|
} else {
|
||||||
Line::from(format!(
|
Line::from(format!(
|
||||||
"{} {} {}",
|
"{} {} {}",
|
||||||
|
@ -350,13 +354,21 @@ impl AppState {
|
||||||
|ListEntry {
|
|ListEntry {
|
||||||
node, has_children, ..
|
node, has_children, ..
|
||||||
}| {
|
}| {
|
||||||
|
let ms_style = if self.multi_select && !node.multi_select {
|
||||||
|
Style::default().fg(self.theme.multi_select_disabled_color())
|
||||||
|
} else {
|
||||||
|
Style::new()
|
||||||
|
};
|
||||||
if *has_children {
|
if *has_children {
|
||||||
Line::from(" ").style(self.theme.dir_color())
|
Line::from(" ")
|
||||||
|
.style(self.theme.dir_color())
|
||||||
|
.patch_style(ms_style)
|
||||||
} else {
|
} else {
|
||||||
Line::from(format!("{} ", node.task_list))
|
Line::from(format!("{} ", node.task_list))
|
||||||
.alignment(Alignment::Right)
|
.alignment(Alignment::Right)
|
||||||
.style(self.theme.cmd_color())
|
.style(self.theme.cmd_color())
|
||||||
.bold()
|
.bold()
|
||||||
|
.patch_style(ms_style)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -468,6 +480,13 @@ impl AppState {
|
||||||
// enabled, need to clear it to prevent state corruption
|
// enabled, need to clear it to prevent state corruption
|
||||||
if !self.multi_select {
|
if !self.multi_select {
|
||||||
self.selected_commands.clear()
|
self.selected_commands.clear()
|
||||||
|
} else {
|
||||||
|
// Prevents non multi_selectable cmd from being pushed into the selected list
|
||||||
|
if let Some(node) = self.get_selected_node() {
|
||||||
|
if !node.multi_select {
|
||||||
|
self.selected_commands.retain(|cmd| cmd.name != node.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConfirmStatus::Confirm => self.handle_confirm_command(),
|
ConfirmStatus::Confirm => self.handle_confirm_command(),
|
||||||
|
@ -525,41 +544,30 @@ impl AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_multi_select(&mut self) {
|
fn toggle_multi_select(&mut self) {
|
||||||
if self.is_current_tab_multi_selectable() {
|
self.multi_select = !self.multi_select;
|
||||||
self.multi_select = !self.multi_select;
|
if !self.multi_select {
|
||||||
if !self.multi_select {
|
self.selected_commands.clear();
|
||||||
self.selected_commands.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_selection(&mut self) {
|
fn toggle_selection(&mut self) {
|
||||||
if let Some(command) = self.get_selected_node() {
|
if let Some(node) = self.get_selected_node() {
|
||||||
if self.selected_commands.contains(&command) {
|
if node.multi_select {
|
||||||
self.selected_commands.retain(|c| c != &command);
|
if self.selected_commands.contains(&node) {
|
||||||
} else {
|
self.selected_commands.retain(|c| c != &node);
|
||||||
self.selected_commands.push(command);
|
} else {
|
||||||
|
self.selected_commands.push(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_current_tab_multi_selectable(&self) -> bool {
|
|
||||||
let index = self.current_tab.selected().unwrap_or(0);
|
|
||||||
self.tabs
|
|
||||||
.get(index)
|
|
||||||
.map_or(false, |tab| tab.multi_selectable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_items(&mut self) {
|
fn update_items(&mut self) {
|
||||||
self.filter.update_items(
|
self.filter.update_items(
|
||||||
&self.tabs,
|
&self.tabs,
|
||||||
self.current_tab.selected().unwrap(),
|
self.current_tab.selected().unwrap(),
|
||||||
*self.visit_stack.last().unwrap(),
|
*self.visit_stack.last().unwrap(),
|
||||||
);
|
);
|
||||||
if !self.is_current_tab_multi_selectable() {
|
|
||||||
self.multi_select = false;
|
|
||||||
self.selected_commands.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks either the current tree node is the root node (can we go up the tree or no)
|
/// Checks either the current tree node is the root node (can we go up the tree or no)
|
||||||
|
|
|
@ -28,6 +28,13 @@ impl Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn multi_select_disabled_color(&self) -> Color {
|
||||||
|
match self {
|
||||||
|
Theme::Default => Color::DarkGray,
|
||||||
|
Theme::Compatible => Color::DarkGray,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tab_color(&self) -> Color {
|
pub fn tab_color(&self) -> Color {
|
||||||
match self {
|
match self {
|
||||||
Theme::Default => Color::Rgb(255, 255, 85),
|
Theme::Default => Color::Rgb(255, 255, 85),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user