#!/usr/bin/env bash set -euo pipefail LOG_FILE="$HOME/cachyos-post-install.log" GIT_USER_NAME="Jose Lago" GIT_USER_EMAIL="jose@lago.dev" OFFICIAL_PACKAGES=( brave-browser chromium filezilla thunderbird libreoffice-fresh code docker docker-compose telegram-desktop signal-desktop git base-devel curl wget jq xdg-utils flatpak cifs-utils smbclient ttf-meslo-nerd zsh-completions ghostty fastfetch lm_sensors pciutils hwdata variety python-pywal tela-circle-icon-theme-all zsh ) AUR_PACKAGES=( opencode-bin ulauncher wireguard-gui-bin vesktop-bin sone ) FLATPAK_PACKAGES=( io.gitlab.adhami3310.Impression com.mattjakeman.ExtensionManager ) NAS_HOST="192.168.178.3" NAS_CREDENTIALS_FILE="/etc/samba/credentials-unraid" NAS_MOUNT_BASE="/mnt/nas" NAS_SHARES=( isos Data ) BRAVE_POLICY_DIR="/etc/brave/policies/managed" BRAVE_POLICY_FILE="$BRAVE_POLICY_DIR/extensions.json" BITWARDEN_EXTENSION_ID="nngceckbapebfimnlniiiahkandclblb" FLOCCUS_EXTENSION_ID="fnaicdffflnofjppbagibeoednhnbjhg" CHROME_WEBSTORE_UPDATE_URL="https://clients2.google.com/service/update2/crx" ORCHIS_REPO_URL="https://github.com/vinceliuice/Orchis-theme.git" ORCHIS_SOURCE_DIR="$HOME/.cache/Orchis-theme" ORCHIS_THEME_NAME="Orchis-Grey-Dark" ORCHIS_THEME_VARIANT="grey" ORCHIS_COLOR_VARIANT="dark" ORCHIS_SIZE_VARIANT="standard" GNOME_ICON_THEME="Adwaita" GNOME_CURSOR_THEME="Adwaita" GNOME_COLOR_SCHEME="prefer-dark" GNOME_ACCENT_COLOR_PRIMARY="slate" GNOME_ACCENT_COLOR_FALLBACK="grey" log() { echo -e "\n==> $*" | tee -a "$LOG_FILE" } warn() { echo -e "\n[WARN] $*" | tee -a "$LOG_FILE" } die() { echo -e "\n[ERROR] $*" | tee -a "$LOG_FILE" exit 1 } require_not_root() { if [[ "${EUID}" -eq 0 ]]; then die "Do not run this script as root. Run it as your normal user; the script will use sudo when needed." fi } check_cachyos() { log "Checking system..." if [[ -f /etc/os-release ]]; then # shellcheck disable=SC1091 source /etc/os-release echo "Detected OS: ${PRETTY_NAME:-unknown}" | tee -a "$LOG_FILE" fi if ! command -v pacman >/dev/null 2>&1; then die "pacman is not available. This script is intended for CachyOS/Arch." fi } full_system_update() { log "Synchronizing package databases..." sudo pacman -Sy --noconfirm if pacman -Qu >/dev/null 2>&1; then log "System updates found. Updating full system..." sudo pacman -Syu --noconfirm else log "No system updates found. Skipping full upgrade." fi } install_drivers_with_chwd() { log "Installing/configuring drivers with CachyOS Hardware Detection..." if ! command -v chwd >/dev/null 2>&1; then warn "chwd is not installed. Trying to install it." sudo pacman -S --needed --noconfirm chwd || warn "Could not install chwd. Continuing without driver autoconfiguration." fi if command -v chwd >/dev/null 2>&1; then log "Showing hardware detected by chwd..." chwd --list || true log "Running chwd autoconfiguration." sudo chwd --autoconfigure || warn "chwd could not autoconfigure something. Review the log: $LOG_FILE" fi } install_official_packages() { log "Checking official packages..." missing_packages=() for package in "${OFFICIAL_PACKAGES[@]}"; do if pacman -Q "$package" >/dev/null 2>&1; then log "Already installed: $package" else missing_packages+=("$package") fi done if (( ${#missing_packages[@]} > 0 )); then log "Installing missing official packages: ${missing_packages[*]}" sudo pacman -S --needed --noconfirm "${missing_packages[@]}" else log "All official packages are already installed. Skipping." fi } ensure_yay() { if command -v yay >/dev/null 2>&1; then log "yay is already installed." return fi log "Installing yay from AUR..." tmpdir="$(mktemp -d)" git clone https://aur.archlinux.org/yay-bin.git "$tmpdir/yay-bin" pushd "$tmpdir/yay-bin" >/dev/null makepkg -si --noconfirm popd >/dev/null rm -rf "$tmpdir" } install_aur_packages() { ensure_yay log "Checking AUR packages..." missing_aur_packages=() for package in "${AUR_PACKAGES[@]}"; do if pacman -Q "$package" >/dev/null 2>&1; then log "Already installed: $package" else missing_aur_packages+=("$package") fi done if (( ${#missing_aur_packages[@]} > 0 )); then log "Installing missing AUR packages: ${missing_aur_packages[*]}" yay -S --needed --noconfirm "${missing_aur_packages[@]}" else log "All AUR packages are already installed. Skipping." fi } install_flatpak_packages() { log "Checking Flatpak and Flathub..." if ! command -v flatpak >/dev/null 2>&1; then warn "flatpak is not available. Skipping Flatpak apps." return fi if ! flatpak remotes --columns=name | grep -qx "flathub"; then log "Adding Flathub remote..." sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo else log "Flathub remote already configured." fi for flatpak_app in "${FLATPAK_PACKAGES[@]}"; do if flatpak info "$flatpak_app" >/dev/null 2>&1; then log "Already installed Flatpak: $flatpak_app" else log "Installing Flatpak: $flatpak_app" flatpak install -y flathub "$flatpak_app" fi done } setup_docker() { log "Enabling Docker..." sudo systemctl enable --now docker if ! groups "$USER" | grep -q "\bdocker\b"; then sudo usermod -aG docker "$USER" warn "Added your user to the docker group. Log out and back in, or reboot, to use docker without sudo." else log "User already belongs to docker group." fi docker --version | tee -a "$LOG_FILE" || true } set_brave_default() { log "Setting Brave as default browser..." if command -v xdg-settings >/dev/null 2>&1; then current_browser="$(xdg-settings get default-web-browser 2>/dev/null || true)" if [[ "$current_browser" == "brave-browser.desktop" ]]; then log "Brave is already the default browser." else xdg-settings set default-web-browser brave-browser.desktop || warn "Could not set default browser with xdg-settings." fi fi xdg-mime default brave-browser.desktop x-scheme-handler/http || true xdg-mime default brave-browser.desktop x-scheme-handler/https || true xdg-mime default brave-browser.desktop text/html || true log "Brave configured as default browser." } install_brave_extensions_policy() { log "Checking Brave extension policy..." desired_policy="$(mktemp)" cat > "$desired_policy" </dev/null 2>&1; then warn "git is not available. Skipping Orchis installation." return fi if [[ -d "$ORCHIS_SOURCE_DIR/.git" ]]; then log "Orchis source already exists. Updating repository..." git -C "$ORCHIS_SOURCE_DIR" pull --ff-only || warn "Could not update Orchis repository. Continuing with existing copy." else log "Cloning Orchis theme repository..." rm -rf "$ORCHIS_SOURCE_DIR" git clone --depth 1 "$ORCHIS_REPO_URL" "$ORCHIS_SOURCE_DIR" fi if [[ ! -x "$ORCHIS_SOURCE_DIR/install.sh" ]]; then chmod +x "$ORCHIS_SOURCE_DIR/install.sh" fi if [[ -d "$HOME/.themes/$ORCHIS_THEME_NAME" ]]; then log "Orchis theme already exists: $HOME/.themes/$ORCHIS_THEME_NAME" log "Re-running installer only to ensure GTK4/libadwaita links and tweaks are correct." fi ( cd "$ORCHIS_SOURCE_DIR" ./install.sh \ --dest "$HOME/.themes" \ --theme "$ORCHIS_THEME_VARIANT" \ --color "$ORCHIS_COLOR_VARIANT" \ --size "$ORCHIS_SIZE_VARIANT" \ --libadwaita \ --tweaks solid compact black primary submenu dock ) log "Orchis installation step completed." } apply_gnome_theme_and_accent() { log "Applying GNOME theme, shell theme, and accent color..." if ! command -v gsettings >/dev/null 2>&1; then warn "gsettings is not available. Skipping GNOME theme configuration." return fi set_gsetting_if_available() { local schema="$1" local key="$2" local value="$3" if gsettings writable "$schema" "$key" >/dev/null 2>&1; then current_value="$(gsettings get "$schema" "$key" 2>/dev/null || true)" if [[ "$current_value" == "" ]]; then log "Already set: $schema $key = $value" else log "Setting: $schema $key = $value" gsettings set "$schema" "$key" "$value" fi else warn "GSettings key not available or not writable: $schema $key" fi } set_gsetting_if_available org.gnome.desktop.interface color-scheme "$GNOME_COLOR_SCHEME" detected_orchis_theme="$ORCHIS_THEME_NAME" if [[ ! -d "$HOME/.themes/$detected_orchis_theme" ]]; then detected_orchis_theme="$(find "$HOME/.themes" -maxdepth 1 -type d -name "Orchis-Grey-Dark*" -printf "%f " 2>/dev/null | sort | head -n 1 || true)" fi if [[ -z "$detected_orchis_theme" ]]; then warn "Could not find an installed Orchis-Grey-Dark theme under $HOME/.themes. Skipping GTK theme application." else log "Using detected Orchis theme: $detected_orchis_theme" set_gsetting_if_available org.gnome.desktop.interface gtk-theme "$detected_orchis_theme" set_gsetting_if_available org.gnome.desktop.wm.preferences theme "$detected_orchis_theme" fi set_gsetting_if_available org.gnome.desktop.interface icon-theme "$GNOME_ICON_THEME" set_gsetting_if_available org.gnome.desktop.interface cursor-theme "$GNOME_CURSOR_THEME" if gsettings writable org.gnome.desktop.interface accent-color >/dev/null 2>&1; then accent_range="$(gsettings range org.gnome.desktop.interface accent-color 2>/dev/null || true)" if echo "$accent_range" | grep -qw "$GNOME_ACCENT_COLOR_PRIMARY"; then set_gsetting_if_available org.gnome.desktop.interface accent-color "$GNOME_ACCENT_COLOR_PRIMARY" elif echo "$accent_range" | grep -qw "$GNOME_ACCENT_COLOR_FALLBACK"; then set_gsetting_if_available org.gnome.desktop.interface accent-color "$GNOME_ACCENT_COLOR_FALLBACK" else warn "GNOME accent color grey/slate is not available on this system. Available range: $accent_range" fi else warn "GNOME accent-color key is not available. Skipping GNOME accent setting." fi if command -v gnome-extensions >/dev/null 2>&1; then if gnome-extensions list | grep -q "user-theme@gnome-shell-extensions.gcampax.github.com"; then gnome-extensions enable user-theme@gnome-shell-extensions.gcampax.github.com || warn "Could not enable User Themes extension." if [[ -n "${detected_orchis_theme:-}" ]]; then shell_css="$HOME/.themes/$detected_orchis_theme/gnome-shell/gnome-shell.css" if [[ -f "$shell_css" ]]; then set_gsetting_if_available org.gnome.shell.extensions.user-theme name "$detected_orchis_theme" warn "GNOME Shell theme is configured. On Wayland, log out and back in, or reboot, to see it fully applied." else warn "Skipping GNOME Shell theme because $shell_css does not exist." fi fi else warn "User Themes extension is not available. GTK theme applied, but GNOME Shell theme may not apply." fi else warn "gnome-extensions command not available. Skipping GNOME Shell theme activation." fi if command -v flatpak >/dev/null 2>&1; then log "Applying Flatpak GTK theme filesystem overrides..." sudo flatpak override --filesystem=xdg-config/gtk-3.0 || warn "Could not apply Flatpak GTK3 override." sudo flatpak override --filesystem=xdg-config/gtk-4.0 || warn "Could not apply Flatpak GTK4 override." fi log "GNOME theme applied with Orchis grey variant." } setup_autologin_for_lago() { log "Checking whether user lago exists for GNOME autologin..." if id "lago" >/dev/null 2>&1; then log "User lago exists. Enabling GDM autologin for lago." sudo mkdir -p /etc/gdm if [[ ! -f /etc/gdm/custom.conf ]]; then sudo tee /etc/gdm/custom.conf >/dev/null </dev/null </dev/null 2>&1; then warn "mount.cifs is not available. Make sure cifs-utils is installed. Skipping NAS mount setup." return fi sudo mkdir -p /etc/samba sudo chmod 755 /etc/samba if [[ ! -f "$NAS_CREDENTIALS_FILE" ]]; then warn "NAS credentials file does not exist yet: $NAS_CREDENTIALS_FILE" warn "Creating a template. Edit it later with: sudo micro $NAS_CREDENTIALS_FILE" sudo tee "$NAS_CREDENTIALS_FILE" >/dev/null </dev/null; then warn "Could not access or create $mount_point. Trying to clear stale mount handle..." sudo umount -lf "$mount_point" 2>/dev/null || true sudo systemctl reset-failed 2>/dev/null || true sudo mkdir -p "$mount_point" fi fstab_line="$remote_share $mount_point cifs credentials=$NAS_CREDENTIALS_FILE,uid=$(id -u),gid=$(id -g),iocharset=utf8,vers=3.0,_netdev,nofail,x-systemd.automount,x-systemd.idle-timeout=60,x-systemd.device-timeout=10s 0 0" if grep -qsE "^//$NAS_HOST/$share[[:space:]]+$mount_point[[:space:]]+cifs[[:space:]]" /etc/fstab; then log "fstab entry already exists for $remote_share -> $mount_point" else log "Adding fstab entry for $remote_share -> $mount_point" echo "$fstab_line" | sudo tee -a /etc/fstab >/dev/null fi done sudo systemctl daemon-reload log "NAS automount entries configured." warn "After editing credentials, test with: sudo mount -a" warn "Mount paths: $NAS_MOUNT_BASE/isos and $NAS_MOUNT_BASE/Data" } remove_ptyxis_and_konsole_install_ghostty() { log "Replacing Ptyxis/Konsole with Ghostty..." if pacman -Q ptyxis >/dev/null 2>&1; then log "Ptyxis is installed. Removing it..." sudo pacman -Rns --noconfirm ptyxis || warn "Could not remove Ptyxis. It may be required by another package." else log "Ptyxis is not installed. Skipping removal." fi if pacman -Q konsole >/dev/null 2>&1; then log "Konsole is installed. Removing it..." sudo pacman -Rns --noconfirm konsole || warn "Could not remove Konsole. It may be required by another package." else log "Konsole is not installed. Skipping removal." fi if pacman -Q ghostty >/dev/null 2>&1; then log "Ghostty is already installed." else log "Installing Ghostty..." sudo pacman -S --needed --noconfirm ghostty fi } configure_ghostty() { log "Configuring Ghostty..." ghostty_config_dir="$HOME/.config/ghostty" ghostty_config="$ghostty_config_dir/config" mkdir -p "$ghostty_config_dir" if [[ -f "$ghostty_config" ]]; then cp "$ghostty_config" "$ghostty_config.backup.$(date +%Y%m%d%H%M%S)" fi cat > "$ghostty_config" </dev/null 2>&1; then warn "zsh is not available. Skipping shell customization." return fi if [[ ! -d "$HOME/.oh-my-zsh" ]]; then log "Installing Oh My Zsh non-interactively..." RUNZSH=no CHSH=no KEEP_ZSHRC=yes sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" else log "Oh My Zsh is already installed." fi zsh_custom="$HOME/.oh-my-zsh/custom" p10k_dir="$zsh_custom/themes/powerlevel10k" mkdir -p "$zsh_custom/themes" if [[ -d "$p10k_dir/.git" ]]; then log "Powerlevel10k already exists. Updating..." git -C "$p10k_dir" pull --ff-only || warn "Could not update Powerlevel10k. Continuing with existing copy." else log "Installing Powerlevel10k into $p10k_dir..." rm -rf "$p10k_dir" git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "$p10k_dir" fi if [[ ! -f "$p10k_dir/powerlevel10k.zsh-theme" ]]; then warn "Powerlevel10k theme file was not found after installation: $p10k_dir/powerlevel10k.zsh-theme" return fi if [[ -f "$HOME/.zshrc" ]]; then cp "$HOME/.zshrc" "$HOME/.zshrc.backup.$(date +%Y%m%d%H%M%S)" fi # Keep the zshrc deterministic. This avoids Oh My Zsh loading before ZSH_CUSTOM # is defined, which causes: theme 'powerlevel10k/powerlevel10k' not found. cat > "$HOME/.zshrc" <<'ZSHRC' # Disable Powerlevel10k instant prompt because this setup intentionally prints # Fastfetch system information at terminal startup. typeset -g POWERLEVEL9K_INSTANT_PROMPT=off export ZSH="$HOME/.oh-my-zsh" ZSH_CUSTOM="$ZSH/custom" ZSH_THEME="powerlevel10k/powerlevel10k" plugins=( git sudo ) source "$ZSH/oh-my-zsh.sh" # Jose fastfetch terminal summary # Disable with: export DISABLE_FASTFETCH=1 if [[ -o interactive ]] && [[ -z "${DISABLE_FASTFETCH:-}" ]] && [[ -z "${FASTFETCH_SHOWN:-}" ]] && command -v fastfetch >/dev/null 2>&1; then export FASTFETCH_SHOWN=1 fastfetch echo fi # Powerlevel10k user config. [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh ZSHRC current_shell="$(getent passwd "$USER" | cut -d: -f7 || true)" zsh_path="$(command -v zsh)" if [[ "$current_shell" != "$zsh_path" ]]; then log "Changing default shell to zsh for $USER..." chsh -s "$zsh_path" "$USER" || warn "Could not change default shell. You can run: chsh -s $zsh_path" else log "Default shell is already zsh." fi log "Zsh + Oh My Zsh + Powerlevel10k configured." warn "Open Ghostty after reboot/login. Run 'p10k configure' if you want to customize the prompt." } configure_wireguard_gui_webkit_workaround() { log "Configuring WireGuard GUI WebKit renderer workaround..." if ! command -v wireguard-gui >/dev/null 2>&1; then warn "wireguard-gui is not installed. Skipping workaround." return fi mkdir -p "$HOME/.local/bin" cat > "$HOME/.local/bin/wireguard-gui-fixed" < "$HOME/.local/bin/wire" </dev/null 2>&1; then log "Adding $HOME/.local/bin to fish user paths..." fish -c "fish_add_path -U $HOME/.local/bin" || warn "Could not add ~/.local/bin to fish PATH." fi if [[ -f "$HOME/.zshrc" ]] && ! grep -q ".local/bin" "$HOME/.zshrc"; then log "Adding $HOME/.local/bin to .zshrc PATH..." printf "\nexport PATH=\"\$HOME/.local/bin:\$PATH\"\n" >> "$HOME/.zshrc" fi fi mkdir -p "$HOME/.local/share/applications" desktop_file="$(find /usr/share/applications "$HOME/.local/share/applications" -name "*wireguard*gui*.desktop" 2>/dev/null | head -n 1 || true)" if [[ -n "$desktop_file" ]]; then cp "$desktop_file" "$HOME/.local/share/applications/wireguard-gui.desktop" sed -i "s|^Exec=.*|Exec=env WEBKIT_DISABLE_DMABUF_RENDERER=1 wireguard-gui|" "$HOME/.local/share/applications/wireguard-gui.desktop" update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true log "WireGuard GUI desktop launcher patched with WEBKIT_DISABLE_DMABUF_RENDERER=1." else warn "Could not find WireGuard GUI desktop file to patch." fi log "WireGuard GUI wrappers created:" log " $HOME/.local/bin/wireguard-gui-fixed" log " $HOME/.local/bin/wire" } configure_fastfetch_terminal_summary() { log "Configuring Fastfetch terminal system summary..." if ! command -v fastfetch >/dev/null 2>&1; then warn "fastfetch is not installed. Skipping terminal system summary." return fi fastfetch_config_dir="$HOME/.config/fastfetch" fastfetch_config="$fastfetch_config_dir/config.jsonc" mkdir -p "$fastfetch_config_dir" if [[ -f "$fastfetch_config" ]]; then cp "$fastfetch_config" "$fastfetch_config.backup.$(date +%Y%m%d%H%M%S)" fi cat > "$fastfetch_config" <<'FASTFETCHCONFIG' { "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", "logo": { "type": "auto" }, "modules": [ "title", "separator", "os", "host", "kernel", "uptime", "shell", "terminal", "cpu", "gpu", "memory", "swap", "disk", "localip", "battery", "packages" ] } FASTFETCHCONFIG if [[ -f "$HOME/.zshrc" ]]; then if ! grep -q "Jose fastfetch terminal summary" "$HOME/.zshrc"; then cat >> "$HOME/.zshrc" </dev/null 2>&1; then export FASTFETCH_SHOWN=1 fastfetch echo fi ZSHFASTFETCH log "Fastfetch startup block added to ~/.zshrc." else log "Fastfetch startup block already exists in ~/.zshrc." fi else warn "~/.zshrc does not exist. Skipping Fastfetch shell startup block." fi log "Fastfetch config written to: $fastfetch_config" } configure_git_identity() { log "Configuring global Git identity..." if ! command -v git >/dev/null 2>&1; then warn "git is not available. Skipping Git identity configuration." return fi current_git_name="$(git config --global --get user.name || true)" current_git_email="$(git config --global --get user.email || true)" if [[ "$current_git_name" == "$GIT_USER_NAME" ]]; then log "Git user.name already set to: $GIT_USER_NAME" else git config --global user.name "$GIT_USER_NAME" log "Git user.name set to: $GIT_USER_NAME" fi if [[ "$current_git_email" == "$GIT_USER_EMAIL" ]]; then log "Git user.email already set to: $GIT_USER_EMAIL" else git config --global user.email "$GIT_USER_EMAIL" log "Git user.email set to: $GIT_USER_EMAIL" fi } configure_ulauncher_launcher() { log "Configuring Ulauncher on Super+Space..." if ! command -v ulauncher >/dev/null 2>&1; then warn "ulauncher is not installed. Skipping launcher configuration." return fi # Free Super+Space from GNOME input-source switching so Ulauncher can use it. gsettings set org.gnome.desktop.wm.keybindings switch-input-source "['space']" || true gsettings set org.gnome.desktop.wm.keybindings switch-input-source-backward "['space']" || true custom_path="/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom-ulauncher/" existing_bindings="$(gsettings get org.gnome.settings-daemon.plugins.media-keys custom-keybindings 2>/dev/null || echo "@as []")" if ! echo "$existing_bindings" | grep -q "$custom_path"; then if [[ "$existing_bindings" == "@as []" ]]; then new_bindings="['$custom_path']" else new_bindings="$(echo "$existing_bindings" | sed "s|]$|, '$custom_path']|")" fi gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "$new_bindings" fi gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:"$custom_path" name "Ulauncher App Launcher" gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:"$custom_path" command "ulauncher-toggle" gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:"$custom_path" binding "space" mkdir -p "$HOME/.config/autostart" if [[ -f /usr/share/applications/ulauncher.desktop ]]; then cp /usr/share/applications/ulauncher.desktop "$HOME/.config/autostart/ulauncher.desktop" else cat > "$HOME/.config/autostart/ulauncher.desktop" <<'ULAUNCHERDESKTOP' [Desktop Entry] Type=Application Name=Ulauncher Exec=ulauncher --hide-window X-GNOME-Autostart-enabled=true ULAUNCHERDESKTOP fi if ! pgrep -x ulauncher >/dev/null 2>&1; then nohup ulauncher --hide-window >/dev/null 2>&1 & fi log "Ulauncher configured on Super+Space." warn "If Super+Space still changes keyboard input source, change GNOME's input-source shortcut in Settings > Keyboard > Typing." } configure_wallpaper_palette_tools() { log "Configuring wallpaper and palette tools..." mkdir -p "$HOME/Pictures/Wallpapers" if command -v variety >/dev/null 2>&1; then log "Variety installed. Use it to find and rotate high-quality wallpapers." else warn "Variety is not available." fi if command -v wal >/dev/null 2>&1; then log "Pywal installed. Generate a palette with: wal -i ~/Pictures/Wallpapers/example.jpg" else warn "Pywal/wal is not available." fi log "Wallpaper folder prepared: $HOME/Pictures/Wallpapers" } configure_gnome_keyboard_shortcuts() { log "Configuring GNOME keyboard shortcuts..." if ! command -v gsettings >/dev/null 2>&1; then warn "gsettings is not available. Skipping GNOME shortcuts." return fi # Keep Super+Space free for Search Light. gsettings set org.gnome.desktop.wm.keybindings switch-input-source "['space']" || true gsettings set org.gnome.desktop.wm.keybindings switch-input-source-backward "['space']" || true # Hyprland-like close shortcut while preserving Alt+F4. gsettings set org.gnome.desktop.wm.keybindings close "['q', 'F4']" || true python - <<'PYSHORTCUTS' import ast import subprocess SCHEMA = "org.gnome.settings-daemon.plugins.media-keys" BASE = "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/" shortcuts = [ ("custom-ghostty", "Open Ghostty", "ghostty", "Return"), ("custom-brave", "Open Brave", "brave-browser", "b"), ("custom-vscode", "Open VS Code", "code", "e"), ("custom-files", "Open Files", "nautilus", "f"), ("custom-mail", "Open Thunderbird", "thunderbird", "m"), ("custom-telegram", "Open Telegram", "telegram-desktop", "t"), ("custom-signal", "Open Signal", "signal-desktop", "s"), ("custom-wireguard", "Open WireGuard GUI", "wireguard-gui-fixed", "w"), ("custom-impression", "Open Impression", "flatpak run io.gitlab.adhami3310.Impression", "i"), ] def get(cmd): return subprocess.check_output(cmd, text=True).strip() def set_value(schema, key, value): subprocess.run(["gsettings", "set", schema, key, value], check=True) raw = get(["gsettings", "get", SCHEMA, "custom-keybindings"]) existing = [] if raw == "@as []" else ast.literal_eval(raw) for shortcut_id, name, command, binding in shortcuts: path = f"{BASE}{shortcut_id}/" if path not in existing: existing.append(path) shortcut_schema = f"{SCHEMA}.custom-keybinding:{path}" set_value(shortcut_schema, "name", name) set_value(shortcut_schema, "command", command) set_value(shortcut_schema, "binding", binding) formatted = "[" + ", ".join(repr(item) for item in existing) + "]" set_value(SCHEMA, "custom-keybindings", formatted) PYSHORTCUTS log "GNOME keyboard shortcuts configured." warn "Search Light should own Super+Space. Clipboard history should own Super+V." } configure_tela_circle_icons() { log "Configuring Tela Circle icon theme..." if [[ -d /usr/share/icons/Tela-circle ]] || [[ -d /usr/share/icons/Tela-circle-dark ]]; then if gsettings writable org.gnome.desktop.interface icon-theme >/dev/null 2>&1; then if [[ -d /usr/share/icons/Tela-circle-dark ]]; then gsettings set org.gnome.desktop.interface icon-theme "Tela-circle-dark" || true log "GNOME icon theme set to Tela-circle-dark." else gsettings set org.gnome.desktop.interface icon-theme "Tela-circle" || true log "GNOME icon theme set to Tela-circle." fi else warn "GNOME icon-theme setting is not writable." fi else warn "Tela Circle icon theme was not found under /usr/share/icons." fi } post_checks() { log "Quick checks..." echo "Brave:" | tee -a "$LOG_FILE" command -v brave-browser | tee -a "$LOG_FILE" || true echo "Chromium:" | tee -a "$LOG_FILE" command -v chromium | tee -a "$LOG_FILE" || true echo "VS Code:" | tee -a "$LOG_FILE" command -v code | tee -a "$LOG_FILE" || true echo "Docker:" | tee -a "$LOG_FILE" docker --version | tee -a "$LOG_FILE" || true echo "Vesktop:" | tee -a "$LOG_FILE" command -v vesktop | tee -a "$LOG_FILE" || true echo "Sone:" | tee -a "$LOG_FILE" command -v sone | tee -a "$LOG_FILE" || true echo "OpenCode:" | tee -a "$LOG_FILE" command -v opencode | tee -a "$LOG_FILE" || true echo "Extension Manager:" | tee -a "$LOG_FILE" flatpak info com.mattjakeman.ExtensionManager >/dev/null 2>&1 && echo "com.mattjakeman.ExtensionManager" | tee -a "$LOG_FILE" || true echo "Tela Circle icons:" | tee -a "$LOG_FILE" ls -d /usr/share/icons/Tela-circle* 2>/dev/null | head -n 10 | tee -a "$LOG_FILE" || true log "Done. Log saved at: $LOG_FILE" } main() { require_not_root check_cachyos full_system_update install_drivers_with_chwd install_official_packages configure_git_identity remove_ptyxis_and_konsole_install_ghostty configure_ghostty configure_zsh_ohmyzsh_powerlevel10k configure_fastfetch_terminal_summary configure_gnome_keyboard_shortcuts configure_tela_circle_icons configure_wallpaper_palette_tools install_aur_packages configure_ulauncher_launcher configure_wireguard_gui_webkit_workaround install_flatpak_packages setup_docker setup_unraid_nas_mounts set_brave_default install_brave_extensions_policy setup_autologin_for_lago post_checks echo echo "✅ Post-install completed." echo "🔁 Recommended: reboot to apply kernel, drivers, Docker group membership, Brave policies, GDM autologin, shell changes, and terminal configuration." } main "$@"