mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2025-04-21 11:17:00 +01:00
Merge ad75771799cc451f09694ca3364e7553d485d8bd into efa6ff9cd2eb77204028de361be008a68338aeb8
This commit is contained in:
commit
009cf75a12
23
README.md
23
README.md
@ -27,25 +27,18 @@ curl -fsSL https://christitus.com/linuxdev | sh
|
|||||||
|
|
||||||
### CLI arguments
|
### CLI arguments
|
||||||
|
|
||||||
Linutil supports various command-line arguments to customize its behavior. Here are some common arguments you can use:
|
View available options by running:
|
||||||
|
|
||||||
- `-c, --config <CONFIG>` : Path to the configuration file.
|
|
||||||
- `--override-validation` : Show all available options, disregarding compatibility checks (UNSAFE).
|
|
||||||
- `--size-bypass` : Bypass the terminal size limit.
|
|
||||||
- `-y, --skip-confirmation` : Skip confirmation prompt before executing commands.
|
|
||||||
- `-t, --theme <THEME>` : Set the theme to use in the application [default: `default`] [possible values: `default`, `compatible`].
|
|
||||||
- `-h, --help` : Print help.
|
|
||||||
|
|
||||||
For more detailed usage, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://christitus.com/linux | sh -s -- --help
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
linutil --help
|
linutil --help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For installer options:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://christitus.com/linux | sh -s -- --help
|
||||||
|
```
|
||||||
|
|
||||||
## ⬇️ Installation
|
## ⬇️ Installation
|
||||||
|
|
||||||
Linutil is also available as a package in various repositories:
|
Linutil is also available as a package in various repositories:
|
||||||
@ -142,7 +135,7 @@ Docs are now [here](https://github.com/Chris-Titus-Docs/linutil-docs)
|
|||||||
|
|
||||||
## 🏅 Thanks to All Contributors
|
## 🏅 Thanks to All Contributors
|
||||||
|
|
||||||
Thank you to everyone who has contributed to the development of Linutil. Your efforts are greatly appreciated, and you’re helping make this tool better for everyone!
|
Thank you to everyone who has contributed to the development of Linutil. Your efforts are greatly appreciated, and you're helping make this tool better for everyone!
|
||||||
|
|
||||||
[](https://github.com/ChrisTitusTech/linutil/graphs/contributors)
|
[](https://github.com/ChrisTitusTech/linutil/graphs/contributors)
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ enum EntryType {
|
|||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
fn is_supported(&self) -> bool {
|
fn is_supported(&self) -> bool {
|
||||||
self.preconditions.as_deref().map_or(true, |preconditions| {
|
self.preconditions.as_deref().is_none_or(|preconditions| {
|
||||||
preconditions.iter().all(
|
preconditions.iter().all(
|
||||||
|Precondition {
|
|Precondition {
|
||||||
matches,
|
matches,
|
||||||
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
imports_granularity = "Crate"
|
@ -14,7 +14,7 @@ default = ["tips"]
|
|||||||
tips = ["rand"]
|
tips = ["rand"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.20", features = ["derive", "std"], default-features = false }
|
clap = { version = "4.5.20", features = ["derive", "std", "help", "usage"], default-features = false }
|
||||||
oneshot = { version = "0.1.8", features = ["std"], default-features = false }
|
oneshot = { version = "0.1.8", features = ["std"], default-features = false }
|
||||||
portable-pty = "0.8.1"
|
portable-pty = "0.8.1"
|
||||||
ratatui = { version = "0.29.0", features = ["crossterm"], default-features = false }
|
ratatui = { version = "0.29.0", features = ["crossterm"], default-features = false }
|
||||||
|
35
tui/src/cli.rs
Normal file
35
tui/src/cli.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::theme::Theme;
|
||||||
|
use clap::Parser;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser, Clone)]
|
||||||
|
pub struct Args {
|
||||||
|
/// Path to the configuration file
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub config: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Set the theme to use in the application
|
||||||
|
#[arg(short, long, value_enum)]
|
||||||
|
#[arg(default_value_t = Theme::Default)]
|
||||||
|
pub theme: Theme,
|
||||||
|
|
||||||
|
/// Skip confirmation prompt before executing commands
|
||||||
|
#[arg(short = 'y', long)]
|
||||||
|
pub skip_confirmation: bool,
|
||||||
|
|
||||||
|
/// Show all available options, disregarding compatibility checks (UNSAFE)
|
||||||
|
#[arg(short = 'u', long)]
|
||||||
|
pub override_validation: bool,
|
||||||
|
|
||||||
|
/// Bypass the terminal size limit
|
||||||
|
#[arg(short = 's', long)]
|
||||||
|
pub size_bypass: bool,
|
||||||
|
|
||||||
|
/// Enable mouse interaction
|
||||||
|
#[arg(short = 'm', long)]
|
||||||
|
pub mouse: bool,
|
||||||
|
|
||||||
|
/// Bypass root user check
|
||||||
|
#[arg(short = 'r', long)]
|
||||||
|
pub bypass_root: bool,
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
mod cli;
|
||||||
mod confirmation;
|
mod confirmation;
|
||||||
mod filter;
|
mod filter;
|
||||||
mod float;
|
mod float;
|
||||||
@ -11,7 +12,7 @@ mod theme;
|
|||||||
#[cfg(feature = "tips")]
|
#[cfg(feature = "tips")]
|
||||||
mod tips;
|
mod tips;
|
||||||
|
|
||||||
use crate::theme::Theme;
|
use crate::cli::Args;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
@ -26,40 +27,17 @@ use ratatui::{
|
|||||||
use state::AppState;
|
use state::AppState;
|
||||||
use std::{
|
use std::{
|
||||||
io::{stdout, Result, Stdout},
|
io::{stdout, Result, Stdout},
|
||||||
path::PathBuf,
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Linux utility toolbox
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
pub struct Args {
|
|
||||||
#[arg(short, long, help = "Path to the configuration file")]
|
|
||||||
config: Option<PathBuf>,
|
|
||||||
#[arg(short, long, value_enum)]
|
|
||||||
#[arg(default_value_t = Theme::Default)]
|
|
||||||
#[arg(help = "Set the theme to use in the application")]
|
|
||||||
theme: Theme,
|
|
||||||
#[arg(
|
|
||||||
short = 'y',
|
|
||||||
long,
|
|
||||||
help = "Skip confirmation prompt before executing commands"
|
|
||||||
)]
|
|
||||||
skip_confirmation: bool,
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
#[clap(help = "Show all available options, disregarding compatibility checks (UNSAFE)")]
|
|
||||||
override_validation: bool,
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
#[clap(help = "Bypass the terminal size limit")]
|
|
||||||
size_bypass: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
let mut state = AppState::new(args.clone());
|
||||||
let mut state = AppState::new(args);
|
|
||||||
|
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
stdout().execute(EnableMouseCapture)?;
|
if args.mouse {
|
||||||
|
stdout().execute(EnableMouseCapture)?;
|
||||||
|
}
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||||
@ -70,7 +48,9 @@ fn main() -> Result<()> {
|
|||||||
// restore terminal
|
// restore terminal
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
terminal.backend_mut().execute(LeaveAlternateScreen)?;
|
terminal.backend_mut().execute(LeaveAlternateScreen)?;
|
||||||
terminal.backend_mut().execute(DisableMouseCapture)?;
|
if args.mouse {
|
||||||
|
terminal.backend_mut().execute(DisableMouseCapture)?;
|
||||||
|
}
|
||||||
terminal.backend_mut().execute(ResetColor)?;
|
terminal.backend_mut().execute(ResetColor)?;
|
||||||
terminal.show_cursor()?;
|
terminal.show_cursor()?;
|
||||||
|
|
||||||
|
@ -8,8 +8,12 @@ This means you have full system access and commands can potentially damage your
|
|||||||
Please proceed with caution and make sure you understand what each script does before executing it.";
|
Please proceed with caution and make sure you understand what each script does before executing it.";
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn check_root_status<'a>() -> Option<FloatingText<'a>> {
|
pub fn check_root_status(bypass_root: bool) -> Option<FloatingText<'static>> {
|
||||||
(Uid::effective().is_root()).then_some(FloatingText::new(
|
if bypass_root {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uid::effective().is_root().then_some(FloatingText::new(
|
||||||
ROOT_WARNING.into(),
|
ROOT_WARNING.into(),
|
||||||
"Root User Warning",
|
"Root User Warning",
|
||||||
true,
|
true,
|
||||||
|
@ -66,6 +66,7 @@ pub struct AppState {
|
|||||||
tip: &'static str,
|
tip: &'static str,
|
||||||
size_bypass: bool,
|
size_bypass: bool,
|
||||||
skip_confirmation: bool,
|
skip_confirmation: bool,
|
||||||
|
mouse_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Focus {
|
pub enum Focus {
|
||||||
@ -96,6 +97,11 @@ enum SelectedItem {
|
|||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new(args: Args) -> Self {
|
pub fn new(args: Args) -> Self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
let root_warning = check_root_status(args.bypass_root);
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
let root_warning = None;
|
||||||
|
|
||||||
let tabs = linutil_core::get_tabs(!args.override_validation);
|
let tabs = linutil_core::get_tabs(!args.override_validation);
|
||||||
let root_id = tabs[0].tree.root().id();
|
let root_id = tabs[0].tree.root().id();
|
||||||
|
|
||||||
@ -122,10 +128,11 @@ impl AppState {
|
|||||||
tip: crate::tips::get_random_tip(),
|
tip: crate::tips::get_random_tip(),
|
||||||
size_bypass: args.size_bypass,
|
size_bypass: args.size_bypass,
|
||||||
skip_confirmation: args.skip_confirmation,
|
skip_confirmation: args.skip_confirmation,
|
||||||
|
mouse_enabled: args.mouse,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if let Some(root_warning) = check_root_status() {
|
if let Some(root_warning) = root_warning {
|
||||||
state.spawn_float(root_warning, FLOAT_SIZE, FLOAT_SIZE);
|
state.spawn_float(root_warning, FLOAT_SIZE, FLOAT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,6 +447,10 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_mouse(&mut self, event: &MouseEvent) -> bool {
|
pub fn handle_mouse(&mut self, event: &MouseEvent) -> bool {
|
||||||
|
if !self.mouse_enabled {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if !self.drawable {
|
if !self.drawable {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -598,23 +609,47 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_down(&mut self) {
|
fn scroll_down(&mut self) {
|
||||||
if let Some(selected) = self.selection.selected() {
|
let Some(selected) = self.selection.selected() else {
|
||||||
if selected == self.filter.item_list().len() - 1 {
|
return;
|
||||||
self.selection.select_first();
|
};
|
||||||
} else {
|
let list_len = if !self.at_root() {
|
||||||
self.selection.select_next();
|
self.filter.item_list().len() + 1
|
||||||
}
|
} else {
|
||||||
}
|
self.filter.item_list().len()
|
||||||
|
};
|
||||||
|
|
||||||
|
if list_len == 0 {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let next_selection = if selected >= list_len - 1 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
selected + 1
|
||||||
|
};
|
||||||
|
self.selection.select(Some(next_selection));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_up(&mut self) {
|
fn scroll_up(&mut self) {
|
||||||
if let Some(selected) = self.selection.selected() {
|
let Some(selected) = self.selection.selected() else {
|
||||||
if selected == 0 {
|
return;
|
||||||
self.selection.select_last();
|
};
|
||||||
} else {
|
let list_len = if !self.at_root() {
|
||||||
self.selection.select_previous();
|
self.filter.item_list().len() + 1
|
||||||
}
|
} else {
|
||||||
}
|
self.filter.item_list().len()
|
||||||
|
};
|
||||||
|
|
||||||
|
if list_len == 0 {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let next_selection = if selected == 0 {
|
||||||
|
list_len - 1
|
||||||
|
} else {
|
||||||
|
selected - 1
|
||||||
|
};
|
||||||
|
self.selection.select(Some(next_selection));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_multi_select(&mut self) {
|
fn toggle_multi_select(&mut self) {
|
||||||
|
@ -2,8 +2,7 @@ use std::fs;
|
|||||||
|
|
||||||
use linutil_core::Command;
|
use linutil_core::Command;
|
||||||
|
|
||||||
use crate::path;
|
use crate::{path, DynError};
|
||||||
use crate::DynError;
|
|
||||||
|
|
||||||
pub const USER_GUIDE: &str = "userguide.md";
|
pub const USER_GUIDE: &str = "userguide.md";
|
||||||
|
|
||||||
|
@ -6,9 +6,10 @@ use std::{env, error::Error};
|
|||||||
type DynError = Box<dyn Error>;
|
type DynError = Box<dyn Error>;
|
||||||
|
|
||||||
pub mod tasks {
|
pub mod tasks {
|
||||||
use crate::docgen::USER_GUIDE;
|
use crate::{
|
||||||
use crate::docgen::{userguide, write};
|
docgen::{userguide, write, USER_GUIDE},
|
||||||
use crate::DynError;
|
DynError,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn docgen() -> Result<(), DynError> {
|
pub fn docgen() -> Result<(), DynError> {
|
||||||
write(USER_GUIDE, &userguide()?);
|
write(USER_GUIDE, &userguide()?);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user