mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-22 05:12:27 +00:00
Compare commits
8 Commits
23c848cea7
...
d0bc7197bc
Author | SHA1 | Date | |
---|---|---|---|
|
d0bc7197bc | ||
|
e3688e9b3d | ||
|
51631a16cb | ||
|
d39ffad527 | ||
|
c0982a787f | ||
|
4f7de594a6 | ||
|
fa2f838b63 | ||
|
99c7f984b4 |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -16,6 +16,7 @@ cd linutil
|
|||
```
|
||||
|
||||
## 3. Make your changes
|
||||
|
||||
- **Edit the files you want to change**: Make your changes to the relevant files.
|
||||
- **Test your changes**: Run `cargo run` to test your modifications in a local environment and ensure everything works as expected.
|
||||
|
||||
|
@ -60,6 +61,7 @@ cd linutil
|
|||
## 11. Documentation
|
||||
|
||||
- **Update the documentation**: If your change affects the functionality, please update the relevant documentation files to reflect this.
|
||||
- **Automatic generation**: If you decide to add functionality through a new shell script, make sure to fill out all fields in `tab_data.toml` and run `cargo xtask docgen`.
|
||||
|
||||
## 12. License
|
||||
|
||||
|
|
2
.github/workflows/github-pages.yml
vendored
2
.github/workflows/github-pages.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
|||
|
||||
- name: Copy Contributing Guidelines
|
||||
run: |
|
||||
echo "<!-- THIS FILE IS GENERATED AUTOMATICALLY. EDIT .github/CONTRIBUTING.md -->\n\n$(cat .github/CONTRIBUTING.md)" > 'docs/contributing.md'
|
||||
echo -e "<!-- THIS FILE IS GENERATED AUTOMATICALLY. EDIT .github/CONTRIBUTING.md -->\n\n$(cat .github/CONTRIBUTING.md)" > 'docs/contributing.md'
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
|
|
|
@ -35,10 +35,10 @@ installLinutil() {
|
|||
printf "%b\n" "${YELLOW}Installing rustup...${RC}"
|
||||
case "$PACKAGER" in
|
||||
pacman)
|
||||
"$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm rustup
|
||||
"$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm curl rustup man-db
|
||||
;;
|
||||
dnf)
|
||||
"$ESCALATION_TOOL" "$PACKAGER" install -y rustup
|
||||
"$ESCALATION_TOOL" "$PACKAGER" install -y curl rustup man-pages man-db man
|
||||
;;
|
||||
zypper)
|
||||
"$ESCALATION_TOOL" "$PACKAGER" install -n curl gcc make
|
||||
|
@ -54,12 +54,23 @@ installLinutil() {
|
|||
rustup default stable
|
||||
cargo install --force linutil_tui
|
||||
printf "%b\n" "${GREEN}Installed successfully.${RC}"
|
||||
installExtra
|
||||
;;
|
||||
*) printf "%b\n" "${RED}Linutil not installed.${RC}" ;;
|
||||
esac
|
||||
esac
|
||||
}
|
||||
|
||||
installExtra() {
|
||||
printf "%b\n" "${YELLOW}Installing the manpage...${RC}"
|
||||
"$ESCALATION_TOOL" mkdir -p /usr/share/man/man1
|
||||
curl 'https://raw.githubusercontent.com/ChrisTitusTech/linutil/refs/heads/main/man/linutil.1' | "$ESCALATION_TOOL" tee '/usr/share/man/man1/linutil.1' > /dev/null
|
||||
printf "%b\n" "${YELLOW}Creating a Desktop Entry...${RC}"
|
||||
"$ESCALATION_TOOL" mkdir -p /usr/share/applications
|
||||
curl 'https://raw.githubusercontent.com/ChrisTitusTech/linutil/refs/heads/main/linutil.desktop' | "$ESCALATION_TOOL" tee /usr/share/applications/linutil.desktop > /dev/null
|
||||
printf "%b\n" "${GREEN}Done.${RC}"
|
||||
}
|
||||
|
||||
checkEnv
|
||||
checkEscalationTool
|
||||
checkAURHelper
|
||||
|
|
|
@ -130,7 +130,7 @@ echo -ne "
|
|||
██║ ██║██║ ██║╚██████╗██║ ██║ ██║ ██║ ██║ ╚██████╔╝███████║
|
||||
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
||||
------------------------------------------------------------------------
|
||||
Please select presetup settings for your system
|
||||
Please select presetup settings for your system
|
||||
------------------------------------------------------------------------
|
||||
"
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ filesystem () {
|
|||
case $? in
|
||||
0) export FS=btrfs;;
|
||||
1) export FS=ext4;;
|
||||
2)
|
||||
2)
|
||||
set_password "LUKS_PASSWORD"
|
||||
export FS=luks
|
||||
;;
|
||||
|
@ -155,14 +155,14 @@ filesystem () {
|
|||
esac
|
||||
}
|
||||
|
||||
# @description Detects and sets timezone.
|
||||
# @description Detects and sets timezone.
|
||||
timezone () {
|
||||
# Added this from arch wiki https://wiki.archlinux.org/title/System_time
|
||||
time_zone="$(curl --fail https://ipapi.co/timezone)"
|
||||
echo -ne "
|
||||
System detected your timezone to be '$time_zone' \n"
|
||||
echo -ne "Is this correct?
|
||||
"
|
||||
"
|
||||
options=("Yes" "No")
|
||||
select_option "${options[@]}"
|
||||
|
||||
|
@ -171,14 +171,14 @@ timezone () {
|
|||
echo "${time_zone} set as timezone"
|
||||
export TIMEZONE=$time_zone;;
|
||||
n|N|no|NO|No)
|
||||
echo "Please enter your desired timezone e.g. Europe/London :"
|
||||
echo "Please enter your desired timezone e.g. Europe/London :"
|
||||
read -r new_timezone
|
||||
echo "${new_timezone} set as timezone"
|
||||
export TIMEZONE=$new_timezone;;
|
||||
*) echo "Wrong option. Try again";timezone;;
|
||||
esac
|
||||
}
|
||||
# @description Set user's keyboard mapping.
|
||||
# @description Set user's keyboard mapping.
|
||||
keymap () {
|
||||
echo -ne "
|
||||
Please select key board layout from this list"
|
||||
|
@ -236,18 +236,18 @@ echo -ne "
|
|||
drivessd
|
||||
}
|
||||
|
||||
# @description Gather username and password to be used for installation.
|
||||
# @description Gather username and password to be used for installation.
|
||||
userinfo () {
|
||||
# Loop through user input until the user gives a valid username
|
||||
while true
|
||||
do
|
||||
do
|
||||
read -r -p "Please enter username: " username
|
||||
if [[ "${username,,}" =~ ^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$ ]]
|
||||
then
|
||||
then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
echo "Incorrect username."
|
||||
done
|
||||
done
|
||||
export USERNAME=$username
|
||||
|
||||
while true
|
||||
|
@ -264,22 +264,22 @@ userinfo () {
|
|||
done
|
||||
export PASSWORD=$PASSWORD1
|
||||
|
||||
# Loop through user input until the user gives a valid hostname, but allow the user to force save
|
||||
# Loop through user input until the user gives a valid hostname, but allow the user to force save
|
||||
while true
|
||||
do
|
||||
do
|
||||
read -r -p "Please name your machine: " name_of_machine
|
||||
# hostname regex (!!couldn't find spec for computer name!!)
|
||||
if [[ "${name_of_machine,,}" =~ ^[a-z][a-z0-9_.-]{0,62}[a-z0-9]$ ]]
|
||||
then
|
||||
break
|
||||
fi
|
||||
then
|
||||
break
|
||||
fi
|
||||
# if validation fails allow the user to force saving of the hostname
|
||||
read -r -p "Hostname doesn't seem correct. Do you still want to save it? (y/n)" force
|
||||
read -r -p "Hostname doesn't seem correct. Do you still want to save it? (y/n)" force
|
||||
if [[ "${force,,}" = "y" ]]
|
||||
then
|
||||
break
|
||||
fi
|
||||
done
|
||||
then
|
||||
break
|
||||
fi
|
||||
done
|
||||
export NAME_OF_MACHINE=$name_of_machine
|
||||
}
|
||||
|
||||
|
@ -351,7 +351,7 @@ echo -ne "
|
|||
Creating Filesystems
|
||||
-------------------------------------------------------------------------
|
||||
"
|
||||
# @description Creates the btrfs subvolumes.
|
||||
# @description Creates the btrfs subvolumes.
|
||||
createsubvolumes () {
|
||||
btrfs subvolume create /mnt/@
|
||||
btrfs subvolume create /mnt/@home
|
||||
|
@ -362,11 +362,11 @@ mountallsubvol () {
|
|||
mount -o "${MOUNT_OPTIONS}",subvol=@home "${partition3}" /mnt/home
|
||||
}
|
||||
|
||||
# @description BTRFS subvolulme creation and mounting.
|
||||
# @description BTRFS subvolulme creation and mounting.
|
||||
subvolumesetup () {
|
||||
# create nonroot subvolumes
|
||||
createsubvolumes
|
||||
# unmount root to remount with subvolume
|
||||
createsubvolumes
|
||||
# unmount root to remount with subvolume
|
||||
umount /mnt
|
||||
# mount @ subvolume
|
||||
mount -o "${MOUNT_OPTIONS}",subvol=@ "${partition3}" /mnt
|
||||
|
@ -386,33 +386,36 @@ fi
|
|||
|
||||
if [[ "${FS}" == "btrfs" ]]; then
|
||||
mkfs.vfat -F32 -n "EFIBOOT" "${partition2}"
|
||||
mkfs.btrfs -L ROOT "${partition3}" -f
|
||||
mkfs.btrfs -f "${partition3}"
|
||||
mount -t btrfs "${partition3}" /mnt
|
||||
subvolumesetup
|
||||
elif [[ "${FS}" == "ext4" ]]; then
|
||||
mkfs.vfat -F32 -n "EFIBOOT" "${partition2}"
|
||||
mkfs.ext4 -L ROOT "${partition3}"
|
||||
mkfs.ext4 "${partition3}"
|
||||
mount -t ext4 "${partition3}" /mnt
|
||||
elif [[ "${FS}" == "luks" ]]; then
|
||||
mkfs.vfat -F32 -n "EFIBOOT" "${partition2}"
|
||||
mkfs.vfat -F32 "${partition2}"
|
||||
# enter luks password to cryptsetup and format root partition
|
||||
echo -n "${LUKS_PASSWORD}" | cryptsetup -y -v luksFormat "${partition3}" -
|
||||
# open luks container and ROOT will be place holder
|
||||
# open luks container and ROOT will be place holder
|
||||
echo -n "${LUKS_PASSWORD}" | cryptsetup open "${partition3}" ROOT -
|
||||
# now format that container
|
||||
mkfs.btrfs -L ROOT "${partition3}"
|
||||
mkfs.btrfs "${partition3}"
|
||||
# create subvolumes for btrfs
|
||||
mount -t btrfs "${partition3}" /mnt
|
||||
subvolumesetup
|
||||
ENCRYPTED_PARTITION_UUID=$(blkid -s UUID -o value "${partition3}")
|
||||
fi
|
||||
|
||||
BOOT_UUID=$(blkid -s UUID -o value "${partition2}")
|
||||
|
||||
sync
|
||||
if ! mountpoint -q /mnt; then
|
||||
echo "ERROR! Failed to mount ${partition3} to /mnt after multiple attempts."
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p /mnt/boot/efi
|
||||
mount -t vfat -L EFIBOOT /mnt/boot/
|
||||
mount -t vfat -U "${BOOT_UUID}" /mnt/boot/
|
||||
|
||||
if ! grep -qs '/mnt' /proc/mounts; then
|
||||
echo "Drive is not mounted can not continue"
|
||||
|
@ -435,8 +438,8 @@ fi
|
|||
echo "keyserver hkp://keyserver.ubuntu.com" >> /mnt/etc/pacman.d/gnupg/gpg.conf
|
||||
cp /etc/pacman.d/mirrorlist /mnt/etc/pacman.d/mirrorlist
|
||||
|
||||
genfstab -L /mnt >> /mnt/etc/fstab
|
||||
echo "
|
||||
genfstab -U /mnt >> /mnt/etc/fstab
|
||||
echo "
|
||||
Generated /etc/fstab:
|
||||
"
|
||||
cat /mnt/etc/fstab
|
||||
|
@ -475,14 +478,14 @@ arch-chroot /mnt /bin/bash -c "KEYMAP='${KEYMAP}' /bin/bash" <<EOF
|
|||
|
||||
echo -ne "
|
||||
-------------------------------------------------------------------------
|
||||
Network Setup
|
||||
Network Setup
|
||||
-------------------------------------------------------------------------
|
||||
"
|
||||
pacman -S --noconfirm --needed networkmanager dhclient
|
||||
systemctl enable --now NetworkManager
|
||||
echo -ne "
|
||||
-------------------------------------------------------------------------
|
||||
Setting up mirrors for optimal download
|
||||
Setting up mirrors for optimal download
|
||||
-------------------------------------------------------------------------
|
||||
"
|
||||
pacman -S --noconfirm --needed pacman-contrib curl
|
||||
|
@ -504,7 +507,7 @@ sed -i "s/COMPRESSXZ=(xz -c -z -)/COMPRESSXZ=(xz -c -T $nc -z -)/g" /etc/makepkg
|
|||
fi
|
||||
echo -ne "
|
||||
-------------------------------------------------------------------------
|
||||
Setup Language to US and set locale
|
||||
Setup Language to US and set locale
|
||||
-------------------------------------------------------------------------
|
||||
"
|
||||
sed -i 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
|
||||
|
@ -572,7 +575,7 @@ echo -ne "
|
|||
-------------------------------------------------------------------------
|
||||
"
|
||||
groupadd libvirt
|
||||
useradd -m -G wheel,libvirt -s /bin/bash $USERNAME
|
||||
useradd -m -G wheel,libvirt -s /bin/bash $USERNAME
|
||||
echo "$USERNAME created, home directory created, added to wheel and libvirt group, default shell set to /bin/bash"
|
||||
echo "$USERNAME:$PASSWORD" | chpasswd
|
||||
echo "$USERNAME password set"
|
||||
|
|
23
core/tabs/system-setup/system-cleanup.sh
Normal file → Executable file
23
core/tabs/system-setup/system-cleanup.sh
Normal file → Executable file
|
@ -26,16 +26,21 @@ cleanup_system() {
|
|||
"$ESCALATION_TOOL" "$PACKAGER" -Rns $(pacman -Qtdq) --noconfirm > /dev/null 2>&1
|
||||
;;
|
||||
*)
|
||||
printf "%b\n" "${RED}Unsupported package manager: ""$PACKAGER""${RC}"
|
||||
return 1
|
||||
printf "%b\n" "${RED}Unsupported package manager: ${PACKAGER}. Skipping.${RC}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
common_cleanup() {
|
||||
"$ESCALATION_TOOL" find /var/tmp -type f -atime +5 -delete
|
||||
"$ESCALATION_TOOL" find /tmp -type f -atime +5 -delete
|
||||
"$ESCALATION_TOOL" find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
|
||||
if [ -d /var/tmp ]; then
|
||||
"$ESCALATION_TOOL" find /var/tmp -type f -atime +5 -delete
|
||||
fi
|
||||
if [ -d /tmp ]; then
|
||||
"$ESCALATION_TOOL" find /tmp -type f -atime +5 -delete
|
||||
fi
|
||||
if [ -d /var/log ]; then
|
||||
"$ESCALATION_TOOL" find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
|
||||
fi
|
||||
"$ESCALATION_TOOL" journalctl --vacuum-time=3d
|
||||
}
|
||||
|
||||
|
@ -45,8 +50,12 @@ clean_data() {
|
|||
case $clean_response in
|
||||
y|Y)
|
||||
printf "%b\n" "${YELLOW}Cleaning up old cache files and emptying trash...${RC}"
|
||||
find "$HOME/.cache/" -type f -atime +5 -delete
|
||||
find "$HOME/.local/share/Trash" -mindepth 1 -delete
|
||||
if [ -d "$HOME/.cache" ]; then
|
||||
find "$HOME/.cache/" -type f -atime +5 -delete
|
||||
fi
|
||||
if [ -d "$HOME/.local/share/Trash" ]; then
|
||||
find "$HOME/.local/share/Trash" -mindepth 1 -delete
|
||||
fi
|
||||
printf "%b\n" "${GREEN}Cache and trash cleanup completed.${RC}"
|
||||
;;
|
||||
*)
|
||||
|
|
|
@ -13,7 +13,7 @@ change_orientation() {
|
|||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b\n" "${YELLOW} Change Monitor Orientation${RC}"
|
||||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b" "${YELLOW}Choose a monitor to configure: ${RC}"
|
||||
printf "%b\n" "${YELLOW}Choose a monitor to configure: ${RC}"
|
||||
i=1
|
||||
for monitor in $monitor_array; do
|
||||
printf "%b\n" "$i. ${GREEN}$monitor${RC}"
|
||||
|
|
|
@ -13,7 +13,7 @@ disable_monitor() {
|
|||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b\n" "${YELLOW} Disable Monitor${RC}"
|
||||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b" "Choose a monitor to disable: "
|
||||
printf "%b\n" "Choose a monitor to disable: "
|
||||
i=1
|
||||
for monitor in $monitor_array; do
|
||||
printf "%b\n" "$i. ${GREEN}$monitor${RC}"
|
||||
|
|
|
@ -13,7 +13,7 @@ enable_monitor() {
|
|||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b\n" "${YELLOW} Enable Monitor${RC}"
|
||||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b" "${YELLOW}Choose a monitor to enable: ${RC}"
|
||||
printf "%b\n" "${YELLOW}Choose a monitor to enable: ${RC}"
|
||||
|
||||
i=1
|
||||
for monitor in $monitor_array; do
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. ./utility_functions.sh
|
||||
. ../utility_functions.sh
|
||||
|
||||
. ../../common-script.sh
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@ manage_arrangement() {
|
|||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b\n" "${YELLOW} Manage Monitor Arrangement${RC}"
|
||||
printf "%b\n" "${YELLOW}=========================================${RC}"
|
||||
printf "%b" "${YELLOW}Choose the monitor to arrange: ${RC}"
|
||||
printf "%b\n" "${YELLOW}Choose the monitor to arrange: ${RC}"
|
||||
i=1
|
||||
for monitor in $monitor_array; do
|
||||
printf "%b\n" "$i. ${YELLOW}$monitor${RC}"
|
||||
printf "%b\n" "$i. ${GREEN}$monitor${RC}"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ set_primary_monitor() {
|
|||
printf "%b\n" "${YELLOW}Choose a monitor to set as primary:${RC}"
|
||||
i=1
|
||||
for monitor in $monitor_array; do
|
||||
printf "%b\n" "$i. ${YELLOW}$monitor${RC}"
|
||||
printf "%b\n" "$i. ${GREEN}$monitor${RC}"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<!-- THIS FILE IS GENERATED AUTOMATICALLY. EDIT .github/CONTRIBUTING.md -->\n\n# Contributing Guidelines for Linutil
|
||||
<!-- THIS FILE IS GENERATED AUTOMATICALLY. EDIT .github/CONTRIBUTING.md -->
|
||||
|
||||
# Contributing Guidelines for Linutil
|
||||
|
||||
Thank you for considering contributing to Linutil! We appreciate your effort in helping improve this project. To ensure that your contributions align with the goals and quality standards of Linutil, please follow these guidelines:
|
||||
|
||||
|
@ -16,6 +18,7 @@ cd linutil
|
|||
```
|
||||
|
||||
## 3. Make your changes
|
||||
|
||||
- **Edit the files you want to change**: Make your changes to the relevant files.
|
||||
- **Test your changes**: Run `cargo run` to test your modifications in a local environment and ensure everything works as expected.
|
||||
|
||||
|
@ -60,6 +63,7 @@ cd linutil
|
|||
## 11. Documentation
|
||||
|
||||
- **Update the documentation**: If your change affects the functionality, please update the relevant documentation files to reflect this.
|
||||
- **Automatic generation**: If you decide to add functionality through a new shell script, make sure to fill out all fields in `tab_data.toml` and run `cargo xtask docgen`.
|
||||
|
||||
## 12. License
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{state::ListEntry, theme::Theme};
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use ego_tree::NodeId;
|
||||
use linutil_core::Tab;
|
||||
use ratatui::{
|
||||
|
@ -116,21 +116,27 @@ impl Filter {
|
|||
pub fn handle_key(&mut self, event: &KeyEvent) -> SearchAction {
|
||||
//Insert user input into the search bar
|
||||
match event.code {
|
||||
KeyCode::Char('c') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
return self.exit_search()
|
||||
}
|
||||
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,
|
||||
KeyCode::Esc => return self.exit_search(),
|
||||
_ => return SearchAction::None,
|
||||
};
|
||||
SearchAction::Update
|
||||
}
|
||||
|
||||
fn exit_search(&mut self) -> SearchAction {
|
||||
self.input_position = 0;
|
||||
self.search_input.clear();
|
||||
SearchAction::Exit
|
||||
}
|
||||
|
||||
fn cursor_left(&mut self) -> SearchAction {
|
||||
self.input_position = self.input_position.saturating_sub(1);
|
||||
SearchAction::None
|
||||
|
|
|
@ -119,7 +119,10 @@ impl AppState {
|
|||
match self.focus {
|
||||
Focus::Search => (
|
||||
"Search bar",
|
||||
Box::new([Shortcut::new("Finish search", ["Enter"])]),
|
||||
Box::new([
|
||||
Shortcut::new("Abort search", ["Esc", "CTRL-c"]),
|
||||
Shortcut::new("Search", ["Enter"]),
|
||||
]),
|
||||
),
|
||||
|
||||
Focus::List => {
|
||||
|
|
Loading…
Reference in New Issue
Block a user