hyprConfig
gitlabE-ink themed Hyprland dotfiles with Quickshell top bar and dynamic matugen color generation.
clone
https://gitlab.com/bitzzdev/hyprConfig.gitdotfilese-inkhyprlandlinux-desktopmatugenquickshellricewayland
#!/usr/bin/env bashset -euo pipefailusage() {cat <<'EOF'Arch install helper for this repo.What it does:1) Installs packages (pacman + optional AUR helper)2) Symlinks dotfiles into $HOME (via stow)Usage:./install-arch.sh./install-arch.sh --dry-run./install-arch.sh --no-packages./install-arch.sh --no-stow./install-arch.sh --forceEnvironment:AUR_HELPER=paru|yay (optional; autodetected if unset)STOW_TARGET=/path (optional; defaults to $HOME)EOF}dry_run=falsedo_packages=truedo_stow=trueforce=falsewhile [[ $# -gt 0 ]]; docase "$1" in-h|--help) usage; exit 0 ;;--dry-run) dry_run=true; shift ;;--no-packages) do_packages=false; shift ;;--no-stow) do_stow=false; shift ;;--force) force=true; shift ;;*)echo "Unknown arg: $1" >&2usage >&2exit 2;;esacdoneif [[ -r /etc/os-release ]]; then# shellcheck disable=SC1091. /etc/os-releasefiif [[ "${ID_LIKE:-} ${ID:-}" != *"arch"* ]]; thenecho "This installer is for Arch-based distros (ID=$ID, ID_LIKE=${ID_LIKE:-})." >&2exit 2firepo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"cd "$repo_root"stow_target="${STOW_TARGET:-$HOME}"run() {if $dry_run; thenprintf '[dry-run] %q ' "$@"printf '\n'return 0fi"$@"}have_cmd() { command -v "$1" >/dev/null 2>&1; }upsert_ini_kv() {# Minimal INI editor: ensure a section exists and set key=value within it.# Only supports single [Section] blocks; good enough for GTK settings.ini.local file="$1"local section="$2"local key="$3"local value="$4"run mkdir -p "$(dirname -- "$file")"if [[ ! -e "$file" ]]; thenif $dry_run; then return 0; fiprintf '%s\n' "[$section]" "$key=$value" "" >"$file"return 0fiif $dry_run; thenecho "[dry-run] set $file [$section] $key=$value"return 0fiawk -v section="[$section]" -v key="$key" -v value="$value" 'BEGIN { in_section=0; section_found=0; key_set=0 }$0 == section {in_section=1; section_found=1; print; next}/^\[/ {if (in_section && !key_set) { print key "=" value; key_set=1 }in_section=0; print; next}{if (in_section && $0 ~ ("^" key "=")) { print key "=" value; key_set=1; next }}END {if (!section_found) {# Add a blank line before the new section if the file does not already end with one.if (NR > 0) print ""print sectionprint key "=" valueprint ""}if (in_section && !key_set) {print key "=" valueprint ""}}' "$file" >"${file}.tmp.$$"mv -f "${file}.tmp.$$" "$file"}apply_dark_theme_best_effort() {echo "Setting system theme preference to dark (best-effort)..."# GTK: works across most desktops/WMs without needing a running D-Bus session.upsert_ini_kv "$stow_target/.config/gtk-3.0/settings.ini" "Settings" "gtk-application-prefer-dark-theme" "1"upsert_ini_kv "$stow_target/.config/gtk-4.0/settings.ini" "Settings" "gtk-application-prefer-dark-theme" "1"# Force a dark GTK3 theme for apps like Nautilus that may not honor prefer-dark in non-GNOME sessions.upsert_ini_kv "$stow_target/.config/gtk-3.0/settings.ini" "Settings" "gtk-theme-name" "Adwaita-dark"kde_colorscheme_exists() {local name="$1"[[ -r "$stow_target/.local/share/color-schemes/${name}.colors" ]] && return 0[[ -r "/usr/share/color-schemes/${name}.colors" ]] && return 0return 1}# KDE/Qt apps: set a dark-ish color scheme. Prefer requested scheme if present; fall back to BreezeDark.# Note: writing a non-existent scheme (e.g. "Black" on a system without it) often makes KDE apps fall back# to the default Breeze (light), which is confusing.local requested_kde_scheme="${KDE_COLOR_SCHEME:-BreezeDark}"local kde_scheme=""if kde_colorscheme_exists "$requested_kde_scheme"; thenkde_scheme="$requested_kde_scheme"elif kde_colorscheme_exists BreezeDark; thenkde_scheme="BreezeDark"elsekde_scheme=""fiif have_cmd plasma-apply-colorscheme; then[[ -n "$kde_scheme" ]] && XDG_CONFIG_HOME="$stow_target/.config" run plasma-apply-colorscheme "$kde_scheme" >/dev/null 2>&1 || truefiif have_cmd kwriteconfig6; then[[ -n "$kde_scheme" ]] && XDG_CONFIG_HOME="$stow_target/.config" run kwriteconfig6 --file kdeglobals --group General --key ColorScheme "$kde_scheme" >/dev/null 2>&1 || trueelif have_cmd kwriteconfig5; then[[ -n "$kde_scheme" ]] && XDG_CONFIG_HOME="$stow_target/.config" run kwriteconfig5 --file kdeglobals --group General --key ColorScheme "$kde_scheme" >/dev/null 2>&1 || trueelse[[ -n "$kde_scheme" ]] && upsert_ini_kv "$stow_target/.config/kdeglobals" "General" "ColorScheme" "$kde_scheme"fi# GNOME/GTK apps via gsettings, if available (requires a user session bus).if have_cmd gsettings; thenrun gsettings set org.gnome.desktop.interface color-scheme prefer-dark >/dev/null 2>&1 || truerun gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark >/dev/null 2>&1 || truefi}backup_if_conflict() {local target="$1"# If the target resolves inside this repo (e.g. ~/.config/hypr is already symlinked to dotfiles),# never move it aside.local resolved=""resolved="$(readlink -f -- "$target" 2>/dev/null || true)"if [[ -n "$resolved" && "$resolved" == "$repo_root/dotfiles/"* ]]; thenreturn 0fiif $force; then[[ -e "$target" || -L "$target" ]] && run rm -rf -- "$target"return 0fi# If the target exists and is not a symlink, stow will refuse. Back it up.if [[ -e "$target" && ! -L "$target" ]]; thenlocal tsts="$(date +%Y%m%d-%H%M%S)"run mv -f "$target" "${target}.bak.${ts}"fi}force_remove_dotfile_conflicts() {# Remove only leaf paths that stow will link (files/symlinks inside the package),# not directories like ~/.config.local rel=""while IFS= read -r rel; do[[ -n "$rel" ]] || continuerun rm -rf -- "$stow_target/$rel"done < <(cd "$repo_root/dotfiles" && find . -type f -o -type l | sed 's|^\./||')}pick_aur_helper() {if [[ -n "${AUR_HELPER:-}" ]]; thenecho "$AUR_HELPER"return 0fiif have_cmd paru; thenecho parureturn 0fiif have_cmd yay; thenecho yayreturn 0fiecho ""}pacman_install() {local -a pkgs=("$@")if [[ ${#pkgs[@]} -eq 0 ]]; then return 0; fiif ! have_cmd pacman; thenecho "pacman not found." >&2exit 2filocal -a missing=()local pfor p in "${pkgs[@]}"; doif pacman -Qi "$p" >/dev/null 2>&1; thencontinuefimissing+=("$p")doneif [[ ${#missing[@]} -eq 0 ]]; thenecho "pacman: all requested packages already installed."return 0fiif $dry_run; thenrun sudo pacman -S --needed --noconfirm "${missing[@]}"elsesudo pacman -S --needed --noconfirm "${missing[@]}"fi}aur_install() {local helper="$1"shiftlocal -a pkgs=("$@")if [[ -z "$helper" || ${#pkgs[@]} -eq 0 ]]; then return 0; fiif ! have_cmd pacman; thenecho "pacman not found." >&2exit 2filocal -a missing=()local pfor p in "${pkgs[@]}"; doif pacman -Qi "$p" >/dev/null 2>&1; thencontinuefimissing+=("$p")doneif [[ ${#missing[@]} -eq 0 ]]; thenecho "AUR: all requested packages already installed."return 0ficase "$helper" inparu) run paru -S --needed --noconfirm "${missing[@]}" ;;yay) run yay -S --needed --noconfirm "${missing[@]}" ;;*)echo "Unsupported AUR helper: $helper" >&2exit 2;;esac}aur_install_optional() {local helper="$1"shiftlocal -a pkgs=("$@")if [[ -z "$helper" || ${#pkgs[@]} -eq 0 ]]; then return 0; fiif ! aur_install "$helper" "${pkgs[@]}"; thenecho "Warning: optional AUR install failed: ${pkgs[*]}" >&2return 0fi}if $do_packages; thenecho "Installing dependencies..."# Repo packages (best-effort; names may vary by setup)pacman_install \stow \hyprland \kitty \dolphin \grim \slurp \ffmpeg \tesseract \tesseract-data-eng \wl-clipboard \hyprpicker \swaync \libnotify \bibata-cursor-theme \wlsunset \wf-recorder \rofi-wayland \cliphist \hyprlock \cpupower \polkit-gnome \hypridle \ttf-nerd-fonts-symbols \awww \jq \imagemagick \libappindicator-gtk3# GNOME/GTK apps (e.g. Nautilus) in non-GNOME sessions: portals help propagate dark preference.pacman_install \xdg-desktop-portal \xdg-desktop-portal-gtk \xdg-desktop-portal-gnome \xdg-desktop-portal-hyprland \xdg-desktop-portal-wlr# AUR packages (optional; if you already have them, nothing happens)aur_helper="$(pick_aur_helper)"if [[ -z "$aur_helper" ]]; thenecho "No AUR helper found (paru/yay). Skipping AUR packages." >&2echo "If you need them: install paru or yay, then re-run." >&2elseaur_install "$aur_helper" \quickshell-git \matugen \mpvpaper# Optional (font family used by the bar if available)aur_install_optional "$aur_helper" \ttf-google-sansfifiif $do_stow; thenecho "Symlinking dotfiles into: $stow_target"run mkdir -p "$stow_target"apply_dark_theme_best_effort# Default file manager (XDG): Nautilusif have_cmd xdg-mime; thenrun xdg-mime default org.gnome.Nautilus.desktop inode/directory >/dev/null 2>&1 || truerun xdg-mime default org.gnome.Nautilus.desktop application/x-gnome-saved-search >/dev/null 2>&1 || truefi# Ensure helper scripts are executable after symlinking.run chmod +x "$repo_root/dotfiles/.local/bin/eink-wallpaper"run chmod +x "$repo_root/dotfiles/.local/bin/eink-launcher"run chmod +x "$repo_root/dotfiles/.local/bin/eink-hypridle-apply"run chmod +x "$repo_root/dotfiles/.local/bin/eink-notify"run chmod +x "$repo_root/dotfiles/.local/bin/eink-ws"run chmod +x "$repo_root/dotfiles/.local/bin/eink-ocr"run chmod +x "$repo_root/dotfiles/.local/bin/eink-cpu-mode"run chmod +x "$repo_root/dotfiles/.local/bin/discord-tray"run chmod +x "$repo_root/dotfiles/.local/bin/eink-sleep"# Quickshell QML files are loaded directly; no chmod needed.if ! have_cmd stow; thenecho "stow not found. Install it or re-run without --no-packages." >&2exit 2fi# Backup known conflict-prone files (stow refuses to overwrite real files).backup_if_conflict "$stow_target/.config/hypr/hyprlock.conf"if $force; thenecho "Force enabled: removing existing linked paths and re-stowing..."run stow -t "$stow_target" -D dotfiles >/dev/null 2>&1 || trueforce_remove_dotfile_conflictsfirun stow -t "$stow_target" dotfilesrun chmod +x "$stow_target/.local/bin/eink-wallpaper" || truerun chmod +x "$stow_target/.local/bin/eink-launcher" || truerun chmod +x "$stow_target/.local/bin/eink-hypridle-apply" || truerun chmod +x "$stow_target/.local/bin/eink-ocr" || truerun chmod +x "$stow_target/.local/bin/eink-cpu-mode" || truerun chmod +x "$stow_target/.local/bin/discord-tray" || truefiecho "Done."