mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-22 05:12:27 +00:00
Merge branch 'main' into testing-3
This commit is contained in:
commit
9e6c014320
153
Cargo.lock
generated
153
Cargo.lock
generated
|
@ -20,21 +20,6 @@ version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android-tzdata"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android_system_properties"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.14"
|
version = "0.6.14"
|
||||||
|
@ -114,12 +99,6 @@ version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cassowary"
|
name = "cassowary"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -135,32 +114,12 @@ dependencies = [
|
||||||
"rustversion",
|
"rustversion",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chrono"
|
|
||||||
version = "0.4.38"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
|
||||||
dependencies = [
|
|
||||||
"android-tzdata",
|
|
||||||
"iana-time-zone",
|
|
||||||
"js-sys",
|
|
||||||
"num-traits",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-targets 0.52.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.16"
|
version = "4.5.16"
|
||||||
|
@ -220,12 +179,6 @@ dependencies = [
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-foundation-sys"
|
|
||||||
version = "0.8.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.27.0"
|
version = "0.27.0"
|
||||||
|
@ -327,29 +280,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone"
|
|
||||||
version = "0.1.60"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
|
||||||
dependencies = [
|
|
||||||
"android_system_properties",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"iana-time-zone-haiku",
|
|
||||||
"js-sys",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone-haiku"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "include_dir"
|
name = "include_dir"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
|
@ -409,15 +339,6 @@ version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "js-sys"
|
|
||||||
version = "0.3.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
|
||||||
dependencies = [
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -502,15 +423,6 @@ dependencies = [
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
@ -959,7 +871,6 @@ dependencies = [
|
||||||
name = "tui"
|
name = "tui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"ego-tree",
|
"ego-tree",
|
||||||
|
@ -971,6 +882,7 @@ dependencies = [
|
||||||
"tempdir",
|
"tempdir",
|
||||||
"toml",
|
"toml",
|
||||||
"tui-term",
|
"tui-term",
|
||||||
|
"unicode-width",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1064,60 +976,6 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen"
|
|
||||||
version = "0.2.92"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"wasm-bindgen-macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-backend"
|
|
||||||
version = "0.2.92"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"log",
|
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro"
|
|
||||||
version = "0.2.92"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"wasm-bindgen-macro-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro-support"
|
|
||||||
version = "0.2.92"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-backend",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-shared"
|
|
||||||
version = "0.2.92"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "6.0.3"
|
version = "6.0.3"
|
||||||
|
@ -1152,15 +1010,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.52.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.52.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -4,7 +4,6 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.33"
|
|
||||||
clap = { version = "4.5.16", features = ["derive"] }
|
clap = { version = "4.5.16", features = ["derive"] }
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
ego-tree = "0.6.2"
|
ego-tree = "0.6.2"
|
||||||
|
@ -17,7 +16,22 @@ tempdir = "0.3.7"
|
||||||
serde = { version = "1.0.205", features = ["derive"] }
|
serde = { version = "1.0.205", features = ["derive"] }
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
which = "6.0.3"
|
which = "6.0.3"
|
||||||
|
unicode-width = "0.1.13"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
chrono = "0.4.33"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "linutil"
|
name = "linutil"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
debug = false
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
strip = true
|
||||||
|
incremental = false
|
14
SECURITY.md
Normal file
14
SECURITY.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
It is recommended that you run the stable version as this is more tested and used by most. The dev branch is bleed-edge commits that are not well tested and aren't meant to be used in production environments
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| latest | :white_check_mark: |
|
||||||
|
| dev | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
I'd recommend making an Issue for reporting a bug. If you would like privately submit the bug you can email me at contact@christitus.com
|
8
build.rs
8
build.rs
|
@ -1,4 +1,12 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
// Rebuild program if any file in commands directory changes.
|
// Rebuild program if any file in commands directory changes.
|
||||||
println!("cargo:rerun-if-changed=src/commands");
|
println!("cargo:rerun-if-changed=src/commands");
|
||||||
|
// Rerun build script if any code is modified
|
||||||
|
println!("cargo:rerun-if-changed=src");
|
||||||
|
|
||||||
|
// Add current date as a variable to be displayed in the 'Linux Toolbox' text.
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-env=BUILD_DATE={}",
|
||||||
|
chrono::Local::now().format("%Y-%m-%d")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
BIN
build/linutil
BIN
build/linutil
Binary file not shown.
|
@ -5,7 +5,6 @@ makeDWM() {
|
||||||
cd "$HOME" && git clone https://github.com/ChrisTitusTech/dwm-titus.git # CD to Home directory to install dwm-titus
|
cd "$HOME" && git clone https://github.com/ChrisTitusTech/dwm-titus.git # CD to Home directory to install dwm-titus
|
||||||
# This path can be changed (e.g. to linux-toolbox directory)
|
# This path can be changed (e.g. to linux-toolbox directory)
|
||||||
cd dwm-titus/ # Hardcoded path, maybe not the best.
|
cd dwm-titus/ # Hardcoded path, maybe not the best.
|
||||||
$ESCALATION_TOOL ./setup.sh # Run setup
|
|
||||||
$ESCALATION_TOOL make clean install # Run make clean install
|
$ESCALATION_TOOL make clean install # Run make clean install
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,15 +12,294 @@ setupDWM() {
|
||||||
echo "Installing DWM-Titus if not already installed"
|
echo "Installing DWM-Titus if not already installed"
|
||||||
case "$PACKAGER" in # Install pre-Requisites
|
case "$PACKAGER" in # Install pre-Requisites
|
||||||
pacman)
|
pacman)
|
||||||
$ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel libx11 libxinerama libxft imlib2
|
$ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel libx11 libxinerama libxft imlib2 libxcb
|
||||||
|
;;
|
||||||
|
apt)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev libxcb1-dev
|
||||||
|
;;
|
||||||
|
dnf)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" groupinstall -y "Development Tools"
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" install -y libx11-devel libxinerama-devel libxft-devel imlib2-devel libxcb-devel
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
$ESCALATION_TOOL "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev
|
echo "Unsupported package manager: $PACKAGER"
|
||||||
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_nerd_font() {
|
||||||
|
FONT_DIR="$HOME/.local/share/fonts"
|
||||||
|
FONT_ZIP="$FONT_DIR/Meslo.zip"
|
||||||
|
FONT_URL="https://github.com/ryanoasis/nerd-fonts/releases/latest/download/Meslo.zip"
|
||||||
|
FONT_INSTALLED=$(fc-list | grep -i "Meslo")
|
||||||
|
|
||||||
|
# Check if Meslo Nerd-font is already installed
|
||||||
|
if [ -n "$FONT_INSTALLED" ]; then
|
||||||
|
echo "Meslo Nerd-fonts are already installed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing Meslo Nerd-fonts"
|
||||||
|
|
||||||
|
# Create the fonts directory if it doesn't exist
|
||||||
|
if [ ! -d "$FONT_DIR" ]; then
|
||||||
|
mkdir -p "$FONT_DIR" || {
|
||||||
|
echo "Failed to create directory: $FONT_DIR"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "$FONT_DIR exists, skipping creation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the font zip file already exists
|
||||||
|
if [ ! -f "$FONT_ZIP" ]; then
|
||||||
|
# Download the font zip file
|
||||||
|
wget -P "$FONT_DIR" "$FONT_URL" || {
|
||||||
|
echo "Failed to download Meslo Nerd-fonts from $FONT_URL"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Meslo.zip already exists in $FONT_DIR, skipping download."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Unzip the font file if it hasn't been unzipped yet
|
||||||
|
if [ ! -d "$FONT_DIR/Meslo" ]; then
|
||||||
|
unzip "$FONT_ZIP" -d "$FONT_DIR" || {
|
||||||
|
echo "Failed to unzip $FONT_ZIP"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Meslo font files already unzipped in $FONT_DIR, skipping unzip."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove the zip file
|
||||||
|
rm "$FONT_ZIP" || {
|
||||||
|
echo "Failed to remove $FONT_ZIP"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rebuild the font cache
|
||||||
|
fc-cache -fv || {
|
||||||
|
echo "Failed to rebuild font cache"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Meslo Nerd-fonts installed successfully"
|
||||||
|
}
|
||||||
|
|
||||||
|
picom_animations() {
|
||||||
|
# Clone the repository in the home/build directory
|
||||||
|
mkdir -p ~/build
|
||||||
|
if [ ! -d ~/build/picom ]; then
|
||||||
|
if ! git clone https://github.com/FT-Labs/picom.git ~/build/picom; then
|
||||||
|
echo "Failed to clone the repository"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Repository already exists, skipping clone"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ~/build/picom || { echo "Failed to change directory to picom"; return 1; }
|
||||||
|
|
||||||
|
# Build the project
|
||||||
|
if ! meson setup --buildtype=release build; then
|
||||||
|
echo "Meson setup failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ninja -C build; then
|
||||||
|
echo "Ninja build failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install the built binary
|
||||||
|
if ! sudo ninja -C build install; then
|
||||||
|
echo "Failed to install the built binary"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Picom animations installed successfully"
|
||||||
|
}
|
||||||
|
|
||||||
|
clone_config_folders() {
|
||||||
|
# Ensure the target directory exists
|
||||||
|
[ ! -d ~/.config ] && mkdir -p ~/.config
|
||||||
|
|
||||||
|
# Iterate over all directories in config/*
|
||||||
|
for dir in config/*/; do
|
||||||
|
# Extract the directory name
|
||||||
|
dir_name=$(basename "$dir")
|
||||||
|
|
||||||
|
# Clone the directory to ~/.config/
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
cp -r "$dir" ~/.config/
|
||||||
|
echo "Cloned $dir_name to ~/.config/"
|
||||||
|
else
|
||||||
|
echo "Directory $dir_name does not exist, skipping"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_backgrounds() {
|
||||||
|
# Set the variable BG_DIR to the path where backgrounds will be stored
|
||||||
|
BG_DIR="$HOME/Pictures/backgrounds"
|
||||||
|
|
||||||
|
# Check if the ~/Pictures directory exists
|
||||||
|
if [ ! -d "~/Pictures" ]; then
|
||||||
|
# If it doesn't exist, print an error message and return with a status of 1 (indicating failure)
|
||||||
|
echo "Pictures directory does not exist"
|
||||||
|
mkdir ~/Pictures
|
||||||
|
echo "Directory was created in Home folder"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the backgrounds directory (BG_DIR) exists
|
||||||
|
if [ ! -d "$BG_DIR" ]; then
|
||||||
|
# If the backgrounds directory doesn't exist, attempt to clone a repository containing backgrounds
|
||||||
|
if ! git clone https://github.com/ChrisTitusTech/nord-background.git ~/Pictures; then
|
||||||
|
# If the git clone command fails, print an error message and return with a status of 1
|
||||||
|
echo "Failed to clone the repository"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# Rename the cloned directory to 'backgrounds'
|
||||||
|
mv ~/Pictures/nord-background ~/Pictures/backgrounds
|
||||||
|
# Print a success message indicating that the backgrounds have been downloaded
|
||||||
|
echo "Downloaded desktop backgrounds to $BG_DIR"
|
||||||
|
else
|
||||||
|
# If the backgrounds directory already exists, print a message indicating that the download is being skipped
|
||||||
|
echo "Path $BG_DIR exists for desktop backgrounds, skipping download of backgrounds"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setupDisplayManager() {
|
||||||
|
echo "Setting up Xorg"
|
||||||
|
case "$PACKAGER" in
|
||||||
|
pacman)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm xorg-xinit xorg-server
|
||||||
|
;;
|
||||||
|
apt)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" install -y xorg xinit
|
||||||
|
;;
|
||||||
|
dnf)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" install -y xorg-x11-xinit xorg-x11-server-Xorg
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported package manager: $PACKAGER"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo "Xorg installed successfully"
|
||||||
|
echo "Setting up Display Manager"
|
||||||
|
currentdm="none"
|
||||||
|
for dm in gdm sddm lightdm; do
|
||||||
|
if systemctl is-active --quiet $dm.service; then
|
||||||
|
currentdm=$dm
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Current display manager: $currentdm"
|
||||||
|
if [ "$currentdm" = "none" ]; then
|
||||||
|
DM="sddm"
|
||||||
|
echo "No display manager found, installing $DM"
|
||||||
|
case "$PACKAGER" in
|
||||||
|
pacman)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm $DM
|
||||||
|
;;
|
||||||
|
apt)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" install -y $DM
|
||||||
|
;;
|
||||||
|
dnf)
|
||||||
|
$ESCALATION_TOOL "$PACKAGER" install -y $DM
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported package manager: $PACKAGER"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo "$DM installed successfully"
|
||||||
|
systemctl enable $DM
|
||||||
|
|
||||||
|
# Clear the screen
|
||||||
|
clear
|
||||||
|
|
||||||
|
# Prompt user for auto-login
|
||||||
|
echo "Do you want to enable auto-login?"
|
||||||
|
echo "Use arrow keys or j/k to navigate, Enter to select"
|
||||||
|
options=("Yes" "No")
|
||||||
|
selected=0
|
||||||
|
|
||||||
|
# Function to print menu
|
||||||
|
print_menu() {
|
||||||
|
for i in "${!options[@]}"; do
|
||||||
|
if [ $i -eq $selected ]; then
|
||||||
|
echo "> ${options[$i]}"
|
||||||
|
else
|
||||||
|
echo " ${options[$i]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Handle user input
|
||||||
|
while true; do
|
||||||
|
print_menu
|
||||||
|
read -rsn1 key
|
||||||
|
case "$key" in
|
||||||
|
$'\x1B') # ESC sequence for arrow keys
|
||||||
|
read -rsn2 key
|
||||||
|
case "$key" in
|
||||||
|
'[A' | 'k') ((selected > 0)) && ((selected--));; # Up arrow or k
|
||||||
|
'[B' | 'j') ((selected < ${#options[@]}-1)) && ((selected++));; # Down arrow or j
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
'') break;; # Enter key
|
||||||
|
esac
|
||||||
|
clear
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${options[$selected]}" = "Yes" ]; then
|
||||||
|
echo "Configuring SDDM for autologin"
|
||||||
|
SDDM_CONF="/etc/sddm.conf"
|
||||||
|
if [ ! -f "$SDDM_CONF" ]; then
|
||||||
|
echo "[Autologin]" | sudo tee -a "$SDDM_CONF"
|
||||||
|
echo "User=$USER" | sudo tee -a "$SDDM_CONF"
|
||||||
|
echo "Session=dwm" | sudo tee -a "$SDDM_CONF"
|
||||||
|
else
|
||||||
|
sudo sed -i '/^\[Autologin\]/d' "$SDDM_CONF"
|
||||||
|
sudo sed -i '/^User=/d' "$SDDM_CONF"
|
||||||
|
sudo sed -i '/^Session=/d' "$SDDM_CONF"
|
||||||
|
echo "[Autologin]" | sudo tee -a "$SDDM_CONF"
|
||||||
|
echo "User=$USER" | sudo tee -a "$SDDM_CONF"
|
||||||
|
echo "Session=dwm" | sudo tee -a "$SDDM_CONF"
|
||||||
|
fi
|
||||||
|
echo "Checking if autologin group exists"
|
||||||
|
if ! getent group autologin > /dev/null; then
|
||||||
|
echo "Creating autologin group"
|
||||||
|
sudo groupadd autologin
|
||||||
|
else
|
||||||
|
echo "Autologin group already exists"
|
||||||
|
fi
|
||||||
|
echo "Adding user with UID 1000 to autologin group"
|
||||||
|
USER_UID_1000=$(getent passwd 1000 | cut -d: -f1)
|
||||||
|
if [ -n "$USER_UID_1000" ]; then
|
||||||
|
sudo usermod -aG autologin "$USER_UID_1000"
|
||||||
|
echo "User $USER_UID_1000 added to autologin group"
|
||||||
|
else
|
||||||
|
echo "No user with UID 1000 found - Auto login not possible"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Auto-login configuration skipped"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
checkEnv
|
checkEnv
|
||||||
checkEscalationTool
|
checkEscalationTool
|
||||||
|
setupDisplayManager
|
||||||
setupDWM
|
setupDWM
|
||||||
makeDWM
|
makeDWM
|
||||||
|
install_nerd_font
|
||||||
|
clone_config_folders
|
||||||
|
configure_backgrounds
|
109
src/commands/gaming/diablo-ii/d2r-loot-filters.sh
Executable file
109
src/commands/gaming/diablo-ii/d2r-loot-filters.sh
Executable file
|
@ -0,0 +1,109 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Search for possible Diablo II Resurrected folder locations
|
||||||
|
echo "Searching for Diablo II Resurrected folders..."
|
||||||
|
possible_paths=$(find $HOME -type d -path "*/drive_c/Program Files (x86)/Diablo II Resurrected" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$possible_paths" ]; then
|
||||||
|
echo "Error: No Diablo II Resurrected folders found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display possible paths and allow selection
|
||||||
|
echo "Possible Diablo II Resurrected folder locations:"
|
||||||
|
mapfile -t paths_array <<< "$possible_paths"
|
||||||
|
selected=0
|
||||||
|
total=${#paths_array[@]}
|
||||||
|
|
||||||
|
print_menu() {
|
||||||
|
clear
|
||||||
|
local max_display=$((total < 10 ? total : 10))
|
||||||
|
local start=$((selected - max_display/2))
|
||||||
|
if ((start < 0)); then start=0; fi
|
||||||
|
if ((start + max_display > total)); then start=$((total - max_display)); fi
|
||||||
|
if ((start < 0)); then start=0; fi
|
||||||
|
|
||||||
|
echo "Please select the Diablo II: Resurrected installation path:"
|
||||||
|
for i in $(seq 0 $((max_display - 1))); do
|
||||||
|
if ((i + start >= total)); then break; fi
|
||||||
|
if [ $((i + start)) -eq $selected ]; then
|
||||||
|
echo "> ${paths_array[$((i + start))]}"
|
||||||
|
else
|
||||||
|
echo " ${paths_array[$((i + start))]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
select_path() {
|
||||||
|
local last_selected=-1
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if [ $last_selected -ne $selected ]; then
|
||||||
|
print_menu
|
||||||
|
last_selected=$selected
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -rsn1 key
|
||||||
|
case "$key" in
|
||||||
|
$'\x1B') # ESC key
|
||||||
|
read -rsn2 key
|
||||||
|
case "$key" in
|
||||||
|
'[A' | 'k')
|
||||||
|
if ((selected > 0)); then
|
||||||
|
((selected--))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'[B' | 'j')
|
||||||
|
if ((selected < total - 1)); then
|
||||||
|
((selected++))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
'') # Enter key
|
||||||
|
d2r_path="${paths_array[$selected]}"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
clear # Clear the screen after selection
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use the select_path function
|
||||||
|
select_path
|
||||||
|
|
||||||
|
# Validate the path
|
||||||
|
if [ ! -d "$d2r_path" ]; then
|
||||||
|
echo "Error: The specified path does not exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the mods folder if it doesn't exist
|
||||||
|
mods_path="$d2r_path/mods"
|
||||||
|
mkdir -p "$mods_path"
|
||||||
|
|
||||||
|
# Download the latest release
|
||||||
|
echo "Downloading the latest loot filter..."
|
||||||
|
wget -q --show-progress https://github.com/ChrisTitusTech/d2r-loot-filter/releases/latest/download/lootfilter.zip -O /tmp/lootfilter.zip
|
||||||
|
|
||||||
|
# Extract the contents to the mods folder
|
||||||
|
echo "Extracting loot filter to $mods_path..."
|
||||||
|
unzip -q -o /tmp/lootfilter.zip -d "$mods_path"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm /tmp/lootfilter.zip
|
||||||
|
|
||||||
|
echo "Loot filter installed successfully in $mods_path"
|
||||||
|
|
||||||
|
# Add instructions for setting launch options
|
||||||
|
echo
|
||||||
|
echo "To complete the setup, please follow these steps to add launch options in Battle.net:"
|
||||||
|
echo "1. Open the Battle.net launcher"
|
||||||
|
echo "2. Select Diablo II: Resurrected"
|
||||||
|
echo "3. Click the gear icon next to the 'Play' button"
|
||||||
|
echo "4. Select 'Game Settings'"
|
||||||
|
echo "5. In the 'Additional command line arguments' field, enter: -mod lootfilter -txt"
|
||||||
|
echo "6. Click 'Done' to save the changes"
|
||||||
|
echo
|
||||||
|
echo "After completing these steps, launch Diablo II: Resurrected through Battle.net to use the loot filter."
|
8
src/commands/gaming/tab_data.toml
Normal file
8
src/commands/gaming/tab_data.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
name = "Gaming"
|
||||||
|
|
||||||
|
[[data]]
|
||||||
|
name = "Diablo II Ressurected"
|
||||||
|
|
||||||
|
[[data.entries]]
|
||||||
|
name = "Loot Filter Install"
|
||||||
|
script = "diablo-ii/d2r-loot-filters.sh"
|
|
@ -5,29 +5,39 @@
|
||||||
fastUpdate() {
|
fastUpdate() {
|
||||||
case ${PACKAGER} in
|
case ${PACKAGER} in
|
||||||
pacman)
|
pacman)
|
||||||
|
|
||||||
checkAURHelper
|
checkAURHelper
|
||||||
$AUR_HELPER -S --needed --noconfirm rate-mirrors-bin
|
$AUR_HELPER -S --needed --noconfirm rate-mirrors-bin
|
||||||
|
|
||||||
if [ -s /etc/pacman.d/mirrorlist ]; then
|
if [ -s /etc/pacman.d/mirrorlist ]; then
|
||||||
$ESCALATION_TOOL cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
|
$ESCALATION_TOOL cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If for some reason DTYPE is still unknown use always arch so the rate-mirrors does not fail
|
# If for some reason DTYPE is still unknown use always arch so the rate-mirrors does not fail
|
||||||
dtype_local=${DTYPE}
|
dtype_local=${DTYPE}
|
||||||
if [ "${DTYPE}" = "unknown" ]; then
|
if [ "${DTYPE}" = "unknown" ]; then
|
||||||
dtype_local="arch"
|
dtype_local="arch"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$ESCALATION_TOOL rate-mirrors --top-mirrors-number-to-retest=5 --disable-comments --save /etc/pacman.d/mirrorlist --allow-root ${dtype_local}
|
$ESCALATION_TOOL rate-mirrors --top-mirrors-number-to-retest=5 --disable-comments --save /etc/pacman.d/mirrorlist --allow-root ${dtype_local}
|
||||||
|
if [ $? -ne 0 ] || [ ! -s /etc/pacman.d/mirrorlist ]; then
|
||||||
|
echo -e "${RED}Rate-mirrors failed, restoring backup.${RC}"
|
||||||
|
$ESCALATION_TOOL cp /etc/pacman.d/mirrorlist.bak /etc/pacman.d/mirrorlist
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
apt-get|nala)
|
apt-get|nala)
|
||||||
$ESCALATION_TOOL apt-get update
|
$ESCALATION_TOOL apt-get update
|
||||||
if ! command_exists nala; then
|
if ! command_exists nala; then
|
||||||
$ESCALATION_TOOL apt-get install -y nala || { echo -e "${YELLOW}Falling back to apt-get${RC}"; PACKAGER="apt-get"; }
|
$ESCALATION_TOOL apt-get install -y nala || { echo -e "${YELLOW}Falling back to apt-get${RC}"; PACKAGER="apt-get"; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${PACKAGER}" = "nala" ]; then
|
if [ "${PACKAGER}" = "nala" ]; then
|
||||||
$ESCALATION_TOOL cp /etc/apt/sources.list /etc/apt/sources.list.bak
|
$ESCALATION_TOOL cp /etc/apt/sources.list /etc/apt/sources.list.bak
|
||||||
$ESCALATION_TOOL nala update
|
$ESCALATION_TOOL nala update
|
||||||
PACKAGER="nala"
|
PACKAGER="nala"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$ESCALATION_TOOL ${PACKAGER} upgrade -y
|
$ESCALATION_TOOL ${PACKAGER} upgrade -y
|
||||||
;;
|
;;
|
||||||
dnf)
|
dnf)
|
||||||
|
@ -90,4 +100,4 @@ checkEnv
|
||||||
checkEscalationTool
|
checkEscalationTool
|
||||||
fastUpdate
|
fastUpdate
|
||||||
updateSystem
|
updateSystem
|
||||||
updateFlatpaks
|
updateFlatpaks
|
|
@ -1 +1,7 @@
|
||||||
directories = ["system-setup", "applications-setup", "security", "utils"]
|
directories = [
|
||||||
|
"applications-setup",
|
||||||
|
"gaming",
|
||||||
|
"security",
|
||||||
|
"system-setup",
|
||||||
|
"utils"
|
||||||
|
]
|
||||||
|
|
|
@ -1,4 +1,33 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ../common-script.sh
|
||||||
|
|
||||||
|
# Function to check bluetoothctl is installed
|
||||||
|
setupBluetooth() {
|
||||||
|
echo "Install bluetoothctl if not already installed..."
|
||||||
|
if ! command_exists bluetoothctl; then
|
||||||
|
case ${PACKAGER} in
|
||||||
|
pacman)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" -S --noconfirm bluez-utils
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" install -y bluez
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
echo "Bluetoothctl is already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if bluetooth service is running
|
||||||
|
if ! systemctl is-active --quiet bluetooth; then
|
||||||
|
echo "Bluetooth service is not running. Starting it now..."
|
||||||
|
$ESCALATION_TOOL systemctl start bluetooth
|
||||||
|
|
||||||
|
if systemctl is-active --quiet bluetooth; then
|
||||||
|
echo "bluetooth service started successfully."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Function to display colored text
|
# Function to display colored text
|
||||||
colored_echo() {
|
colored_echo() {
|
||||||
|
@ -128,4 +157,6 @@ remove_device() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize
|
# Initialize
|
||||||
|
checkEnv
|
||||||
|
setupBluetooth
|
||||||
main_menu
|
main_menu
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to auto-detect displays and set common resolution
|
# Function to auto-detect displays and set common resolution
|
||||||
auto_detect_displays() {
|
auto_detect_displays() {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to change monitor orientation
|
# Function to change monitor orientation
|
||||||
change_orientation() {
|
change_orientation() {
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
clear
|
clear
|
||||||
echo -e "${BLUE}=========================================${RESET}"
|
echo -e "${BLUE}=========================================${RESET}"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
RESET='\033[0m'
|
RESET='\033[0m'
|
||||||
BOLD='\033[1m'
|
BOLD='\033[1m'
|
||||||
|
@ -12,7 +13,7 @@ CYAN='\033[36m'
|
||||||
# Function to disable a monitor
|
# Function to disable a monitor
|
||||||
disable_monitor() {
|
disable_monitor() {
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
clear
|
clear
|
||||||
echo -e "${BLUE}=========================================${RESET}"
|
echo -e "${BLUE}=========================================${RESET}"
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to duplicate displays
|
# Function to duplicate displays
|
||||||
duplicate_displays() {
|
duplicate_displays() {
|
||||||
primary=$(detect_connected_monitors | head -n 1)
|
primary=$(detect_connected_monitors | head -n 1)
|
||||||
for monitor in $(detect_connected_monitors | tail -n +2); do
|
for monitor in $(detect_connected_monitors | tail -n +2); do
|
||||||
if confirm_action "Duplicate $monitor to $primary?"; then
|
if confirm_action "Duplicate $monitor to $primary?"; then
|
||||||
echo "Duplicating $monitor to $primary"
|
echo "Duplicating $monitor to $primary"
|
||||||
execute_command "xrandr --output $monitor --same-as $primary"
|
execute_command "xrandr --output $monitor --same-as $primary"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicate_displays
|
duplicate_displays
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
RESET='\033[0m'
|
RESET='\033[0m'
|
||||||
BOLD='\033[1m'
|
BOLD='\033[1m'
|
||||||
|
@ -12,7 +13,7 @@ CYAN='\033[36m'
|
||||||
# Function to enable a monitor
|
# Function to enable a monitor
|
||||||
enable_monitor() {
|
enable_monitor() {
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
clear
|
clear
|
||||||
echo -e "${BLUE}=========================================${RESET}"
|
echo -e "${BLUE}=========================================${RESET}"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to extend displays
|
# Function to extend displays
|
||||||
extend_displays() {
|
extend_displays() {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to manage monitor arrangement
|
# Function to manage monitor arrangement
|
||||||
manage_arrangement() {
|
manage_arrangement() {
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
clear
|
clear
|
||||||
echo -e "${BLUE}=========================================${RESET}"
|
echo -e "${BLUE}=========================================${RESET}"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to reset scaling back to 1 (native resolution) for all monitors
|
# Function to reset scaling back to 1 (native resolution) for all monitors
|
||||||
reset_scaling() {
|
reset_scaling() {
|
||||||
|
@ -8,7 +9,7 @@ reset_scaling() {
|
||||||
echo -e "${BLUE}=========================================${RESET}"
|
echo -e "${BLUE}=========================================${RESET}"
|
||||||
|
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
for monitor in "${monitor_array[@]}"; do
|
for monitor in "${monitor_array[@]}"; do
|
||||||
echo -e "${CYAN}Resetting scaling for $monitor to 1x1 (native resolution)${RESET}"
|
echo -e "${CYAN}Resetting scaling for $monitor to 1x1 (native resolution)${RESET}"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to scale smaller monitors to the highest resolution of a bigger monitor
|
# Function to scale smaller monitors to the highest resolution of a bigger monitor
|
||||||
scale_monitors() {
|
scale_monitors() {
|
||||||
|
@ -8,7 +9,7 @@ scale_monitors() {
|
||||||
echo -e "${BLUE}=========================================${RESET}"
|
echo -e "${BLUE}=========================================${RESET}"
|
||||||
|
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
# Get the highest resolution among all monitors
|
# Get the highest resolution among all monitors
|
||||||
max_width=0
|
max_width=0
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
# Function to set a monitor as primary
|
# Function to set a monitor as primary
|
||||||
set_primary_monitor() {
|
set_primary_monitor() {
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
clear
|
clear
|
||||||
echo -e "${BLUE}=========================================${RESET}"
|
echo -e "${BLUE}=========================================${RESET}"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
source ./utility_functions.sh
|
|
||||||
|
. ./utility_functions.sh
|
||||||
|
|
||||||
RESET='\033[0m'
|
RESET='\033[0m'
|
||||||
BOLD='\033[1m'
|
BOLD='\033[1m'
|
||||||
|
@ -12,7 +13,7 @@ CYAN='\033[36m'
|
||||||
# Function to set resolutions
|
# Function to set resolutions
|
||||||
set_resolutions() {
|
set_resolutions() {
|
||||||
monitor_list=$(detect_connected_monitors)
|
monitor_list=$(detect_connected_monitors)
|
||||||
IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list"
|
IFS=$'\n' read -r -a monitor_array <<<"$monitor_list"
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
clear
|
clear
|
||||||
|
|
|
@ -1,4 +1,39 @@
|
||||||
#!/bin/bash
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ../../common-script.sh
|
||||||
|
|
||||||
|
# Function to check bluetoothctl is installed
|
||||||
|
setup_xrandr() {
|
||||||
|
echo "Install xrandr if not already installed..."
|
||||||
|
if ! command_exists xrandr; then
|
||||||
|
case ${PACKAGER} in
|
||||||
|
pacman)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" -S --noconfirm xorg-xrandr
|
||||||
|
;;
|
||||||
|
apt-get)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" install -y x11-xserver-utils
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" install -y xorg-x11-server-utils
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
echo "xrandr is already installed."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to display colored text
|
||||||
|
colored_echo() {
|
||||||
|
local color=$1
|
||||||
|
local text=$2
|
||||||
|
case $color in
|
||||||
|
red) echo -e "\033[31m$text\033[0m" ;;
|
||||||
|
green) echo -e "\033[32m$text\033[0m" ;;
|
||||||
|
yellow) echo -e "\033[33m$text\033[0m" ;;
|
||||||
|
blue) echo -e "\033[34m$text\033[0m" ;;
|
||||||
|
*) echo "$text" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
# Function to execute xrandr commands and handle errors
|
# Function to execute xrandr commands and handle errors
|
||||||
execute_command() {
|
execute_command() {
|
||||||
|
@ -47,3 +82,6 @@ confirm_action() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkEnv
|
||||||
|
setup_xrandr
|
|
@ -1,12 +1,15 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ../common-script.sh
|
||||||
|
|
||||||
# setleds can be used in all distros
|
# setleds can be used in all distros
|
||||||
# This method works by calling a script using systemd service
|
# This method works by calling a script using systemd service
|
||||||
|
|
||||||
# Create a script to toggle numlock
|
# Create a script to toggle numlock
|
||||||
|
|
||||||
create_file() {
|
create_file() {
|
||||||
echo "Creating script..."
|
echo "Creating script..."
|
||||||
sudo tee "/usr/local/bin/numlock" >/dev/null <<'EOF'
|
$ESCALATION_TOOL tee "/usr/local/bin/numlock" >/dev/null <<'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
for tty in /dev/tty{1..6}
|
for tty in /dev/tty{1..6}
|
||||||
|
@ -15,13 +18,13 @@ do
|
||||||
done
|
done
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
sudo chmod +x /usr/local/bin/numlock
|
$ESCALATION_TOOL chmod +x /usr/local/bin/numlock
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create a systemd service to run the script on boot
|
# Create a systemd service to run the script on boot
|
||||||
create_service() {
|
create_service() {
|
||||||
echo "Creating service..."
|
echo "Creating service..."
|
||||||
sudo tee "/etc/systemd/system/numlock.service" >/dev/null <<'EOF'
|
$ESCALATION_TOOL tee "/etc/systemd/system/numlock.service" >/dev/null <<'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=numlock
|
Description=numlock
|
||||||
|
|
||||||
|
@ -35,7 +38,7 @@ WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
numlockSetup() {
|
||||||
# Check if the script and service files exists
|
# Check if the script and service files exists
|
||||||
if [ ! -f "/usr/local/bin/numlock" ]; then
|
if [ ! -f "/usr/local/bin/numlock" ]; then
|
||||||
create_file
|
create_file
|
||||||
|
@ -48,13 +51,14 @@ main() {
|
||||||
printf "Do you want to enable Numlock on boot? (y/n): "
|
printf "Do you want to enable Numlock on boot? (y/n): "
|
||||||
read -r confirm
|
read -r confirm
|
||||||
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
|
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
|
||||||
sudo systemctl enable numlock.service --quiet
|
$ESCALATION_TOOL systemctl enable numlock.service --quiet
|
||||||
echo "Numlock will be enabled on boot"
|
echo "Numlock will be enabled on boot"
|
||||||
else
|
else
|
||||||
sudo systemctl disable numlock.service --quiet
|
$ESCALATION_TOOL systemctl disable numlock.service --quiet
|
||||||
echo "Numlock will not be enabled on boot"
|
echo "Numlock will not be enabled on boot"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
checkEscalationTool
|
||||||
|
numlockSetup
|
||||||
|
|
|
@ -25,10 +25,6 @@ matches = true
|
||||||
data = { environment = "DISPLAY" }
|
data = { environment = "DISPLAY" }
|
||||||
values = [":0", ":1", ":2", ":3", ":4", ":5", ":6", ":7", ":8", ":9"]
|
values = [":0", ":1", ":2", ":3", ":4", ":5", ":6", ":7", ":8", ":9"]
|
||||||
|
|
||||||
[[data.entries]]
|
|
||||||
name = "Set Resolution"
|
|
||||||
script = "monitor-control/set_resolutions.sh"
|
|
||||||
|
|
||||||
[[data.entries]]
|
[[data.entries]]
|
||||||
name = "Duplicate Displays"
|
name = "Duplicate Displays"
|
||||||
script = "monitor-control/duplicate_displays.sh"
|
script = "monitor-control/duplicate_displays.sh"
|
||||||
|
|
|
@ -1,5 +1,36 @@
|
||||||
@@ -0,0 +1,168 @@
|
#!/bin/sh -e
|
||||||
#!/bin/bash
|
|
||||||
|
. ../common-script.sh
|
||||||
|
|
||||||
|
# Function to check if NetworkManager is installed
|
||||||
|
setupNetworkManager() {
|
||||||
|
echo "Install NetworkManger if not already installed..."
|
||||||
|
if ! command_exists nmcli; then
|
||||||
|
case ${PACKAGER} in
|
||||||
|
pacman)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" -S --noconfirm networkmanager
|
||||||
|
;;
|
||||||
|
dnf)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" install -y NetworkManager-1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
$ESCALATION_TOOL "${PACKAGER}" install -y network-manager
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
echo "NetworkManager is already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if NetworkManager service is running
|
||||||
|
if ! systemctl is-active --quiet NetworkManager; then
|
||||||
|
echo "NetworkManager service is not running. Starting it now..."
|
||||||
|
$ESCALATION_TOOL systemctl start NetworkManager
|
||||||
|
|
||||||
|
if systemctl is-active --quiet NetworkManager; then
|
||||||
|
echo "NetworkManager service started successfully."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Function to display colored text
|
# Function to display colored text
|
||||||
colored_echo() {
|
colored_echo() {
|
||||||
|
@ -166,4 +197,6 @@ remove_network() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize
|
# Initialize
|
||||||
|
checkEnv
|
||||||
|
setupNetworkManager
|
||||||
main_menu
|
main_menu
|
||||||
|
|
160
src/filter.rs
Normal file
160
src/filter.rs
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
use crate::{state::ListEntry, tabs::Tab, theme::Theme};
|
||||||
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
|
use ego_tree::NodeId;
|
||||||
|
use ratatui::{
|
||||||
|
layout::Rect,
|
||||||
|
style::Style,
|
||||||
|
text::Span,
|
||||||
|
widgets::{Block, Borders, Paragraph},
|
||||||
|
Frame,
|
||||||
|
};
|
||||||
|
use unicode_width::UnicodeWidthChar;
|
||||||
|
|
||||||
|
pub enum SearchAction {
|
||||||
|
None,
|
||||||
|
Exit,
|
||||||
|
Update,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Filter {
|
||||||
|
search_input: Vec<char>,
|
||||||
|
in_search_mode: bool,
|
||||||
|
input_position: usize,
|
||||||
|
items: Vec<ListEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
search_input: vec![],
|
||||||
|
in_search_mode: false,
|
||||||
|
input_position: 0,
|
||||||
|
items: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn item_list(&self) -> &[ListEntry] {
|
||||||
|
&self.items
|
||||||
|
}
|
||||||
|
pub fn activate_search(&mut self) {
|
||||||
|
self.in_search_mode = true;
|
||||||
|
}
|
||||||
|
pub fn deactivate_search(&mut self) {
|
||||||
|
self.in_search_mode = false;
|
||||||
|
}
|
||||||
|
pub fn update_items(&mut self, tabs: &[Tab], current_tab: usize, node: NodeId) {
|
||||||
|
if self.search_input.is_empty() {
|
||||||
|
let curr = tabs[current_tab].tree.get(node).unwrap();
|
||||||
|
|
||||||
|
self.items = curr
|
||||||
|
.children()
|
||||||
|
.map(|node| ListEntry {
|
||||||
|
node: node.value().clone(),
|
||||||
|
id: node.id(),
|
||||||
|
has_children: node.has_children(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
} else {
|
||||||
|
self.items.clear();
|
||||||
|
|
||||||
|
let query_lower = self.search_input.iter().collect::<String>().to_lowercase();
|
||||||
|
for tab in tabs.iter() {
|
||||||
|
let mut stack = vec![tab.tree.root().id()];
|
||||||
|
while let Some(node_id) = stack.pop() {
|
||||||
|
let node = tab.tree.get(node_id).unwrap();
|
||||||
|
|
||||||
|
if node.value().name.to_lowercase().contains(&query_lower)
|
||||||
|
&& !node.has_children()
|
||||||
|
{
|
||||||
|
self.items.push(ListEntry {
|
||||||
|
node: node.value().clone(),
|
||||||
|
id: node.id(),
|
||||||
|
has_children: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.extend(node.children().map(|child| child.id()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.items.sort_by(|a, b| a.node.name.cmp(&b.node.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn draw_searchbar(&self, frame: &mut Frame, area: Rect, theme: &Theme) {
|
||||||
|
//Set the search bar text (If empty use the placeholder)
|
||||||
|
let display_text = if !self.in_search_mode && self.search_input.is_empty() {
|
||||||
|
Span::raw("Press / to search")
|
||||||
|
} else {
|
||||||
|
Span::raw(self.search_input.iter().collect::<String>())
|
||||||
|
};
|
||||||
|
|
||||||
|
let search_color = if self.in_search_mode {
|
||||||
|
theme.focused_color()
|
||||||
|
} else {
|
||||||
|
theme.unfocused_color()
|
||||||
|
};
|
||||||
|
|
||||||
|
//Create the search bar widget
|
||||||
|
let search_bar = Paragraph::new(display_text)
|
||||||
|
.block(Block::default().borders(Borders::ALL).title("Search"))
|
||||||
|
.style(Style::default().fg(search_color));
|
||||||
|
|
||||||
|
//Render the search bar (First chunk of the screen)
|
||||||
|
frame.render_widget(search_bar, area);
|
||||||
|
|
||||||
|
// Render cursor in search bar
|
||||||
|
if self.in_search_mode {
|
||||||
|
let cursor_position: usize = self.search_input[..self.input_position]
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.width().unwrap_or(1))
|
||||||
|
.sum();
|
||||||
|
let x = area.x + cursor_position as u16 + 1;
|
||||||
|
let y = area.y + 1;
|
||||||
|
frame.set_cursor(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handles key events. Returns true if search must be exited
|
||||||
|
pub fn handle_key(&mut self, event: &KeyEvent) -> SearchAction {
|
||||||
|
//Insert user input into the search bar
|
||||||
|
match event.code {
|
||||||
|
KeyCode::Char(c) => self.insert_char(c),
|
||||||
|
KeyCode::Backspace => self.remove_previous(),
|
||||||
|
KeyCode::Delete => self.remove_next(),
|
||||||
|
KeyCode::Left => return self.cursor_left(),
|
||||||
|
KeyCode::Right => return self.cursor_right(),
|
||||||
|
KeyCode::Esc => {
|
||||||
|
self.input_position = 0;
|
||||||
|
self.search_input.clear();
|
||||||
|
return SearchAction::Exit;
|
||||||
|
}
|
||||||
|
KeyCode::Enter => return SearchAction::Exit,
|
||||||
|
_ => return SearchAction::None,
|
||||||
|
};
|
||||||
|
SearchAction::Update
|
||||||
|
}
|
||||||
|
fn cursor_left(&mut self) -> SearchAction {
|
||||||
|
self.input_position = self.input_position.saturating_sub(1);
|
||||||
|
SearchAction::None
|
||||||
|
}
|
||||||
|
fn cursor_right(&mut self) -> SearchAction {
|
||||||
|
if self.input_position < self.search_input.len() {
|
||||||
|
self.input_position += 1;
|
||||||
|
}
|
||||||
|
SearchAction::None
|
||||||
|
}
|
||||||
|
fn insert_char(&mut self, input: char) {
|
||||||
|
self.search_input.insert(self.input_position, input);
|
||||||
|
self.cursor_right();
|
||||||
|
}
|
||||||
|
fn remove_previous(&mut self) {
|
||||||
|
let current = self.input_position;
|
||||||
|
if current > 0 {
|
||||||
|
self.search_input.remove(current - 1);
|
||||||
|
self.cursor_left();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn remove_next(&mut self) {
|
||||||
|
let current = self.input_position;
|
||||||
|
if current < self.search_input.len() {
|
||||||
|
self.search_input.remove(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,8 +68,18 @@ impl FloatContent for FloatingText {
|
||||||
.text
|
.text
|
||||||
.iter()
|
.iter()
|
||||||
.skip(self.scroll)
|
.skip(self.scroll)
|
||||||
|
.flat_map(|line| {
|
||||||
|
if line.is_empty() {
|
||||||
|
return vec![String::new()];
|
||||||
|
}
|
||||||
|
line.chars()
|
||||||
|
.collect::<Vec<char>>()
|
||||||
|
.chunks(inner_area.width as usize)
|
||||||
|
.map(|chunk| chunk.iter().collect())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
})
|
||||||
.take(inner_area.height as usize)
|
.take(inner_area.height as usize)
|
||||||
.map(|line| Line::from(line.as_str()))
|
.map(Line::from)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Create list widget
|
// Create list widget
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod filter;
|
||||||
mod float;
|
mod float;
|
||||||
mod floating_text;
|
mod floating_text;
|
||||||
mod running_command;
|
mod running_command;
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
|
||||||
use ratatui::{
|
|
||||||
layout::Rect,
|
|
||||||
style::Style,
|
|
||||||
text::Span,
|
|
||||||
widgets::{Block, Borders, Paragraph},
|
|
||||||
Frame,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::state::AppState;
|
|
||||||
|
|
||||||
pub struct SearchBar {
|
|
||||||
search_input: String,
|
|
||||||
in_search_mode: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SearchBar {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
SearchBar {
|
|
||||||
search_input: String::new(),
|
|
||||||
in_search_mode: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn activate_search(&mut self) {
|
|
||||||
self.in_search_mode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deactivate_search(&mut self) {
|
|
||||||
self.in_search_mode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_search_active(&self) -> bool {
|
|
||||||
self.in_search_mode
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(&self, frame: &mut Frame, area: Rect, state: &AppState) {
|
|
||||||
//Set the search bar text (If empty use the placeholder)
|
|
||||||
let display_text = if !self.in_search_mode && self.search_input.is_empty() {
|
|
||||||
Span::raw("Press / to search")
|
|
||||||
} else {
|
|
||||||
Span::raw(&self.search_input)
|
|
||||||
};
|
|
||||||
|
|
||||||
//Create the search bar widget
|
|
||||||
let mut search_bar = Paragraph::new(display_text)
|
|
||||||
.block(Block::default().borders(Borders::ALL).title("Search"))
|
|
||||||
.style(Style::default().fg(state.theme.unfocused_color));
|
|
||||||
|
|
||||||
//Change the color if in search mode
|
|
||||||
if self.in_search_mode {
|
|
||||||
search_bar = search_bar
|
|
||||||
.clone()
|
|
||||||
.style(Style::default().fg(state.theme.focused_color));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Render the search bar (First chunk of the screen)
|
|
||||||
frame.render_widget(search_bar, area);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_key(&mut self, event: KeyEvent) -> String {
|
|
||||||
//Insert user input into the search bar
|
|
||||||
match event.code {
|
|
||||||
KeyCode::Char(c) => {
|
|
||||||
self.search_input.push(c);
|
|
||||||
}
|
|
||||||
KeyCode::Backspace => {
|
|
||||||
self.search_input.pop();
|
|
||||||
}
|
|
||||||
KeyCode::Esc => {
|
|
||||||
self.search_input = String::new();
|
|
||||||
self.in_search_mode = false;
|
|
||||||
}
|
|
||||||
KeyCode::Enter => {
|
|
||||||
self.in_search_mode = false;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
self.search_input.clone()
|
|
||||||
}
|
|
||||||
}
|
|
126
src/state.rs
126
src/state.rs
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
filter::{Filter, SearchAction},
|
||||||
float::{Float, FloatContent},
|
float::{Float, FloatContent},
|
||||||
floating_text::FloatingText,
|
floating_text::FloatingText,
|
||||||
running_command::{Command, RunningCommand},
|
running_command::{Command, RunningCommand},
|
||||||
|
@ -9,9 +10,9 @@ use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
|
||||||
use ego_tree::NodeId;
|
use ego_tree::NodeId;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
style::{Color, Style, Stylize},
|
style::{Style, Stylize},
|
||||||
text::{Line, Span},
|
text::Line,
|
||||||
widgets::{Block, Borders, List, ListState, Paragraph},
|
widgets::{Block, Borders, List, ListState},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -25,16 +26,13 @@ pub struct AppState {
|
||||||
tabs: Vec<Tab>,
|
tabs: Vec<Tab>,
|
||||||
/// Current tab
|
/// Current tab
|
||||||
current_tab: ListState,
|
current_tab: ListState,
|
||||||
/// Current search query
|
|
||||||
search_query: String,
|
|
||||||
/// Current items
|
|
||||||
items: Vec<ListEntry>,
|
|
||||||
/// This stack keeps track of our "current dirrectory". You can think of it as `pwd`. but not
|
/// This stack keeps track of our "current dirrectory". You can think of it as `pwd`. but not
|
||||||
/// just the current directory, all paths that took us here, so we can "cd .."
|
/// just the current directory, all paths that took us here, so we can "cd .."
|
||||||
visit_stack: Vec<NodeId>,
|
visit_stack: Vec<NodeId>,
|
||||||
/// This is the state asociated with the list widget, used to display the selection in the
|
/// This is the state asociated with the list widget, used to display the selection in the
|
||||||
/// widget
|
/// widget
|
||||||
selection: ListState,
|
selection: ListState,
|
||||||
|
filter: Filter,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Focus {
|
pub enum Focus {
|
||||||
|
@ -44,10 +42,10 @@ pub enum Focus {
|
||||||
FloatingWindow(Float),
|
FloatingWindow(Float),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ListEntry {
|
pub struct ListEntry {
|
||||||
node: ListNode,
|
pub node: ListNode,
|
||||||
id: NodeId,
|
pub id: NodeId,
|
||||||
has_children: bool,
|
pub has_children: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
|
@ -59,10 +57,9 @@ impl AppState {
|
||||||
focus: Focus::List,
|
focus: Focus::List,
|
||||||
tabs,
|
tabs,
|
||||||
current_tab: ListState::default().with_selected(Some(0)),
|
current_tab: ListState::default().with_selected(Some(0)),
|
||||||
search_query: String::new(),
|
|
||||||
items: vec![],
|
|
||||||
visit_stack: vec![root_id],
|
visit_stack: vec![root_id],
|
||||||
selection: ListState::default().with_selected(Some(0)),
|
selection: ListState::default().with_selected(Some(0)),
|
||||||
|
filter: Filter::new(),
|
||||||
};
|
};
|
||||||
state.update_items();
|
state.update_items();
|
||||||
state
|
state
|
||||||
|
@ -110,20 +107,7 @@ impl AppState {
|
||||||
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
|
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
|
||||||
.split(horizontal[1]);
|
.split(horizontal[1]);
|
||||||
|
|
||||||
// Render search bar
|
self.filter.draw_searchbar(frame, chunks[0], &self.theme);
|
||||||
let search_text = match self.focus {
|
|
||||||
Focus::Search => Span::raw(&self.search_query),
|
|
||||||
_ if !self.search_query.is_empty() => Span::raw(&self.search_query),
|
|
||||||
_ => Span::raw("Press / to search"),
|
|
||||||
};
|
|
||||||
let search_bar = Paragraph::new(search_text)
|
|
||||||
.block(Block::default().borders(Borders::ALL))
|
|
||||||
.style(Style::default().fg(if let Focus::Search = self.focus {
|
|
||||||
Color::Blue
|
|
||||||
} else {
|
|
||||||
Color::DarkGray
|
|
||||||
}));
|
|
||||||
frame.render_widget(search_bar, chunks[0]);
|
|
||||||
|
|
||||||
let mut items: Vec<Line> = Vec::new();
|
let mut items: Vec<Line> = Vec::new();
|
||||||
if !self.at_root() {
|
if !self.at_root() {
|
||||||
|
@ -132,7 +116,7 @@ impl AppState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.extend(self.items.iter().map(
|
items.extend(self.filter.item_list().iter().map(
|
||||||
|ListEntry {
|
|ListEntry {
|
||||||
node, has_children, ..
|
node, has_children, ..
|
||||||
}| {
|
}| {
|
||||||
|
@ -153,10 +137,11 @@ impl AppState {
|
||||||
} else {
|
} else {
|
||||||
Style::new()
|
Style::new()
|
||||||
})
|
})
|
||||||
.block(Block::default().borders(Borders::ALL).title(format!(
|
.block(
|
||||||
"Linux Toolbox - {}",
|
Block::default()
|
||||||
chrono::Local::now().format("%Y-%m-%d")
|
.borders(Borders::ALL)
|
||||||
)))
|
.title(format!("Linux Toolbox - {}", env!("BUILD_DATE"))),
|
||||||
|
)
|
||||||
.scroll_padding(1);
|
.scroll_padding(1);
|
||||||
frame.render_stateful_widget(list, chunks[1], &mut self.selection);
|
frame.render_stateful_widget(list, chunks[1], &mut self.selection);
|
||||||
|
|
||||||
|
@ -171,21 +156,11 @@ impl AppState {
|
||||||
self.focus = Focus::List;
|
self.focus = Focus::List;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Focus::Search => {
|
Focus::Search => match self.filter.handle_key(key) {
|
||||||
match key.code {
|
SearchAction::Exit => self.exit_search(),
|
||||||
KeyCode::Char(c) => self.search_query.push(c),
|
SearchAction::Update => self.update_items(),
|
||||||
KeyCode::Backspace => {
|
_ => {}
|
||||||
self.search_query.pop();
|
},
|
||||||
}
|
|
||||||
KeyCode::Esc => {
|
|
||||||
self.search_query = String::new();
|
|
||||||
self.exit_search();
|
|
||||||
}
|
|
||||||
KeyCode::Enter => self.exit_search(),
|
|
||||||
_ => return true,
|
|
||||||
}
|
|
||||||
self.update_items();
|
|
||||||
}
|
|
||||||
_ if key.code == KeyCode::Char('q') => return false,
|
_ if key.code == KeyCode::Char('q') => return false,
|
||||||
Focus::TabList => match key.code {
|
Focus::TabList => match key.code {
|
||||||
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Tab => {
|
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Tab => {
|
||||||
|
@ -202,8 +177,8 @@ impl AppState {
|
||||||
self.refresh_tab();
|
self.refresh_tab();
|
||||||
}
|
}
|
||||||
KeyCode::Char('/') => self.enter_search(),
|
KeyCode::Char('/') => self.enter_search(),
|
||||||
KeyCode::Char('t') => self.theme = self.theme.next(),
|
KeyCode::Char('t') => self.theme.next(),
|
||||||
KeyCode::Char('T') => self.theme = self.theme.prev(),
|
KeyCode::Char('T') => self.theme.prev(),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Focus::List if key.kind != KeyEventKind::Release => match key.code {
|
Focus::List if key.kind != KeyEventKind::Release => match key.code {
|
||||||
|
@ -220,53 +195,20 @@ impl AppState {
|
||||||
}
|
}
|
||||||
KeyCode::Char('/') => self.enter_search(),
|
KeyCode::Char('/') => self.enter_search(),
|
||||||
KeyCode::Tab => self.focus = Focus::TabList,
|
KeyCode::Tab => self.focus = Focus::TabList,
|
||||||
KeyCode::Char('t') => self.theme = self.theme.next(),
|
KeyCode::Char('t') => self.theme.next(),
|
||||||
KeyCode::Char('T') => self.theme = self.theme.prev(),
|
KeyCode::Char('T') => self.theme.prev(),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
pub fn update_items(&mut self) {
|
fn update_items(&mut self) {
|
||||||
if self.search_query.is_empty() {
|
self.filter.update_items(
|
||||||
let curr = self.tabs[self.current_tab.selected().unwrap()]
|
&self.tabs,
|
||||||
.tree
|
self.current_tab.selected().unwrap(),
|
||||||
.get(*self.visit_stack.last().unwrap())
|
*self.visit_stack.last().unwrap(),
|
||||||
.unwrap();
|
);
|
||||||
|
|
||||||
self.items = curr
|
|
||||||
.children()
|
|
||||||
.map(|node| ListEntry {
|
|
||||||
node: node.value().clone(),
|
|
||||||
id: node.id(),
|
|
||||||
has_children: node.has_children(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
} else {
|
|
||||||
self.items.clear();
|
|
||||||
|
|
||||||
let query_lower = self.search_query.to_lowercase();
|
|
||||||
for tab in self.tabs.iter() {
|
|
||||||
let mut stack = vec![tab.tree.root().id()];
|
|
||||||
while let Some(node_id) = stack.pop() {
|
|
||||||
let node = tab.tree.get(node_id).unwrap();
|
|
||||||
|
|
||||||
if node.value().name.to_lowercase().contains(&query_lower)
|
|
||||||
&& !node.has_children()
|
|
||||||
{
|
|
||||||
self.items.push(ListEntry {
|
|
||||||
node: node.value().clone(),
|
|
||||||
id: node.id(),
|
|
||||||
has_children: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.extend(node.children().map(|child| child.id()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.items.sort_by(|a, b| a.node.name.cmp(&b.node.name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Checks ehther the current tree node is the root node (can we go up the tree or no)
|
/// Checks ehther the current tree node is the root node (can we go up the tree or no)
|
||||||
/// Returns `true` if we can't go up the tree (we are at the tree root)
|
/// Returns `true` if we can't go up the tree (we are at the tree root)
|
||||||
|
@ -292,7 +234,7 @@ impl AppState {
|
||||||
selected_index = selected_index.saturating_sub(1);
|
selected_index = selected_index.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(item) = self.items.get(selected_index) {
|
if let Some(item) = self.filter.item_list().get(selected_index) {
|
||||||
if !item.has_children {
|
if !item.has_children {
|
||||||
return Some(item.node.command.clone());
|
return Some(item.node.command.clone());
|
||||||
} else if change_directory {
|
} else if change_directory {
|
||||||
|
@ -321,11 +263,13 @@ impl AppState {
|
||||||
}
|
}
|
||||||
fn enter_search(&mut self) {
|
fn enter_search(&mut self) {
|
||||||
self.focus = Focus::Search;
|
self.focus = Focus::Search;
|
||||||
|
self.filter.activate_search();
|
||||||
self.selection.select(None);
|
self.selection.select(None);
|
||||||
}
|
}
|
||||||
fn exit_search(&mut self) {
|
fn exit_search(&mut self) {
|
||||||
self.selection.select(Some(0));
|
self.selection.select(Some(0));
|
||||||
self.focus = Focus::List;
|
self.focus = Focus::List;
|
||||||
|
self.filter.deactivate_search();
|
||||||
self.update_items();
|
self.update_items();
|
||||||
}
|
}
|
||||||
fn refresh_tab(&mut self) {
|
fn refresh_tab(&mut self) {
|
||||||
|
|
14
src/theme.rs
14
src/theme.rs
|
@ -86,17 +86,15 @@ impl Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
#[allow(unused)]
|
pub fn next(&mut self) {
|
||||||
pub fn next(self) -> Self {
|
let position = *self as usize;
|
||||||
let position = self as usize;
|
|
||||||
let types = Theme::value_variants();
|
let types = Theme::value_variants();
|
||||||
types[(position + 1) % types.len()].into()
|
*self = types[(position + 1) % types.len()];
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
pub fn prev(&mut self) {
|
||||||
pub fn prev(self) -> Self {
|
let position = *self as usize;
|
||||||
let position = self as usize;
|
|
||||||
let types = Theme::value_variants();
|
let types = Theme::value_variants();
|
||||||
types[(position + types.len() - 1) % types.len()].into()
|
*self = types[(position + types.len() - 1) % types.len()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user