diff --git a/dotfiles/.config/hypr/layout/windowrules.lua b/dotfiles/.config/hypr/layout/windowrules.lua index 791ea00..bc8d8e3 100644 --- a/dotfiles/.config/hypr/layout/windowrules.lua +++ b/dotfiles/.config/hypr/layout/windowrules.lua @@ -100,6 +100,12 @@ hl.window_rule({ name = "windowrule-47", tile = true, match = { class = "^([Cc]h hl.window_rule({ name = "windowrule-48", size = "monitor_w*0.25 monitor_h*0.55", float = true, match = { class = "^(com.network.manager)$" } }) hl.window_rule({ name = "windowrule-49", workspace = "special:nyx", match = { class = "^([Vv]esktop|[Dd]iscord|[Ss]potify)$" } }) hl.window_rule({ name = "windowrule-255", size = "monitor_w*0.75 monitor_h*0.80", float = true, match = { class = "^([Bb]itwarden)$" } }) +-- System updater terminal (quickshell/scripts/update-system.sh) — float + center it instead of +-- tiling into the current workspace. Matches the title the script launches it with, so it works +-- for Ghostty and the foot fallback alike; initial_title so a later rename can't dodge it. +-- Fixed pixel size rather than monitor_w/h fractions: HDMI-A-1 is rotated portrait (1080 wide), +-- where a fraction of the width made it too narrow for package output; 1000x650 fits every screen. +hl.window_rule({ name = "system-update-terminal", float = true, center = true, size = "1000 650", match = { initial_title = "^(update)$" } }) -- viewer tags hl.window_rule({ name = "windowrule-50", tag = "+viewer", match = { class = "^(gnome-system-monitor|org.gnome.SystemMonitor|io.missioncenter.MissionCenter)$" } }) -- system monitor diff --git a/dotfiles/.config/quickshell/scripts/update-system.sh b/dotfiles/.config/quickshell/scripts/update-system.sh index c531175..b7f66e6 100755 --- a/dotfiles/.config/quickshell/scripts/update-system.sh +++ b/dotfiles/.config/quickshell/scripts/update-system.sh @@ -6,38 +6,37 @@ # this machine is Arch, since these dotfiles aren't. # # The window it opens is floated + centered by a matching Hyprland rule -# (system-update-terminal, in .config/hypr/UserConfigs/WindowRules.conf), +# (system-update-terminal, in .config/hypr/layout/windowrules.lua), # same treatment as Bitwarden's window, rather than tiling into whatever # workspace you happened to be on. iDIR="$HOME/.config/swaync/images" -if ! command -v foot &> /dev/null; then - notify-send -i "$iDIR/error.png" "Need foot:" "foot terminal not found. Please install foot terminal." - exit 1 -fi - # Printed inside the terminal before the actual update command runs, so it # reads as "updating" from the first frame rather than looking like an # empty/stuck window while pacman/apt/etc. take a moment to start printing. # -# foot runs this in a fresh `bash -c`, which doesn't inherit shell functions -# from this script's own environment (export -f only reaches bash processes -# forked from *this* one, not whatever foot execs) — so instead, the -# already-colored text is built once here with real escape bytes ($'...'), -# then %q-quoted into a self-contained `echo '...'` that reproduces those -# same bytes verbatim when the child bash parses it. +# The terminal runs this in a fresh `bash -c`, which doesn't inherit shell +# functions from this script's own environment (export -f only reaches bash +# processes forked from *this* one, not whatever the terminal execs) — so +# instead, the already-colored text is built once here with real escape +# bytes ($'...'), then %q-quoted into a self-contained `echo '...'` that +# reproduces those same bytes verbatim when the child bash parses it. msg=" Updating System — please wait " line=$(printf '=%.0s' $(seq 1 ${#msg})) banner_text=$'\033[1;36m'"$line"$'\033[0m\n\033[1;36m'"$msg"$'\033[0m\n\033[1;36m'"$line"$'\033[0m\n\n' BANNER="echo $(printf '%q' "$banner_text")" +# GHOSTTY_INSTALL is how to install the terminal on this distro, if it's +# packaged there at all — only Arch ships it in the official repos. if command -v paru &> /dev/null; then CMD="paru -Syu" LABEL="Arch-based system" + GHOSTTY_INSTALL="pacman -S --needed --noconfirm ghostty" elif command -v yay &> /dev/null; then CMD="yay -Syu" LABEL="Arch-based system" + GHOSTTY_INSTALL="pacman -S --needed --noconfirm ghostty" elif command -v dnf &> /dev/null; then CMD="sudo dnf update --refresh -y" LABEL="Fedora system" @@ -52,15 +51,55 @@ else exit 1 fi +# --- Terminal: Ghostty, offering to install it if missing --- +# This runs from the bar with no terminal of its own, so there's nowhere to +# type a sudo password — the install goes through pkexec instead, which +# pops up the polkit agent's graphical password prompt. Declining (or a +# failed install) falls back to foot for this run, so updating never gets +# blocked on the terminal choice. +if ! command -v ghostty &> /dev/null; then + if [ -n "$GHOSTTY_INSTALL" ]; then + # -A waits for a click and prints the chosen action's name (nothing if + # the notification is dismissed instead). + choice=$(notify-send -i "$iDIR/error.png" -u critical \ + -A install="Install Ghostty" -A foot="Use foot this time" \ + "Ghostty not installed" "The system updater runs in Ghostty. Install it now?") + if [ "$choice" = "install" ]; then + if pkexec $GHOSTTY_INSTALL; then + notify-send -i "$iDIR/profile-pic.jpg" -u low "Ghostty installed" "Starting the update..." + else + notify-send -i "$iDIR/error.png" "Ghostty install failed" "Using foot for this update." + fi + fi + else + notify-send -i "$iDIR/error.png" "Ghostty not installed" \ + "Install Ghostty for the system updater — using foot for now." + fi +fi + +if command -v ghostty &> /dev/null; then + # --gtk-single-instance=false: otherwise, with a Ghostty window already + # open, this would just hand the window off to that running instance and + # return immediately — SystemUpdates.qml relies on this script blocking + # until the update window is closed (that's when it re-checks the count), + # and the "has been updated" notification below would fire up front. + TERMINAL=(ghostty --gtk-single-instance=false --title=update -e) +elif command -v foot &> /dev/null; then + TERMINAL=(foot -T update) +else + notify-send -i "$iDIR/error.png" "Need a terminal:" "Neither Ghostty nor foot is installed." + exit 1 +fi + # Keeps the terminal open (whether the update succeeded or failed — read # always runs regardless, since it's ; not &&) until you press Enter, -# instead of foot closing the instant the update command exits and taking -# any output/errors with it. +# instead of the terminal closing the instant the update command exits and +# taking any output/errors with it. WAIT_PROMPT="echo; read -p 'Update finished — press Enter to close...'" # Pull + re-stow the dotfiles first, so config changes from the other machine # land alongside the package update. SYNC="$(printf '%q' "$(dirname "$0")/sync-dotfiles.sh")" -foot -T update bash -c "$BANNER; $SYNC; $CMD; $WAIT_PROMPT" +"${TERMINAL[@]}" bash -c "$BANNER; $SYNC; $CMD; $WAIT_PROMPT" notify-send -i "$iDIR/profile-pic.jpg" -u low "$LABEL" 'has been updated.'