Compare commits
24
Commits
52e747fe3e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
747b7ebdb7 | ||
|
|
5858eec50d | ||
|
|
8d8dee2342 | ||
|
|
5372c6ea29 | ||
|
|
740586611a | ||
|
|
3211350435 | ||
|
|
933f3a9efd | ||
|
|
06446f36f8 | ||
|
|
49d2f0e41e | ||
|
|
c0c4eadfe1 | ||
|
|
90c22b4e0c | ||
|
|
d316308bf5 | ||
|
|
64afc07250 | ||
|
|
e74c9c5bc1 | ||
|
|
a9b9ba6ba4 | ||
|
|
deeacbe580 | ||
|
|
bfba908ef4 | ||
|
|
ef02250a8f | ||
|
|
e5c20e0a46 | ||
|
|
737d601bca | ||
|
|
00803e59a4 | ||
|
|
3fbdd754c1 | ||
|
|
06204a357a | ||
|
|
2f29cb8bae |
@@ -77,7 +77,7 @@ It runs these steps, in this order, and every one of them is idempotent:
|
||||
| 1. Directories | creates the 19 directories the shell and scripts expect, before anything writes into them |
|
||||
| 2. `BLOB_PATH` | symlinks `~/.local/share/blob` to this checkout |
|
||||
| 3. Packages | installs what is missing from `packages/`, bootstrapping `yay` first on a machine that has no AUR helper |
|
||||
| 4. Config | copies `hypr/`, the themed app configs, `blob/shell.json`, the uwsm environment, and the icon font into `~/.config` |
|
||||
| 4. Config | copies `hypr/`, the themed app configs, `blob/shell.json`, the uwsm environment, and the icon font into `~/.config`, and writes `/etc/profile.d/blob.sh` so a login shell finds the `blob` command |
|
||||
| 5. First theme | links `~/wallpapers` to the checkout, applies `flats` headless, picks a background, and links the btop and Neovim theme files, so the first login is not a blank desktop |
|
||||
| 6. User units | installs the session services into `~/.config/systemd/user` and enables them |
|
||||
| 7. systemd drop-ins | writes the shutdown timeouts, oomd thresholds, plocate prune paths, and sleep hooks under `/etc` |
|
||||
|
||||
+128
-10
@@ -3,7 +3,9 @@
|
||||
# blob-boot - boot splash and bootloader management.
|
||||
#
|
||||
# blob-boot [image] Apply a Plymouth boot splash image (default action)
|
||||
# blob-boot grub Migrate from Limine back to GRUB, detecting every OS
|
||||
# blob-boot grub Install GRUB and detect every other OS on the machine.
|
||||
# Migrates off Limine when it is there; on a machine
|
||||
# that never had it, only GRUB is installed
|
||||
# --purge removes Limine in the same run instead of
|
||||
# keeping it for one fallback boot
|
||||
# blob-boot detect Rescan for other operating systems, rebuild the menu
|
||||
@@ -16,8 +18,12 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DEFAULT_IMAGE="$HOME/Documents/dotfiles/branding/boot_flash.png"
|
||||
PLYMOUTH_LOGO="/usr/share/plymouth/themes/blob/logo.png"
|
||||
BLOB_PATH="${BLOB_PATH:-$HOME/.local/share/blob}"
|
||||
DEFAULT_IMAGE="$BLOB_PATH/branding/boot_flash.png"
|
||||
THEME_SOURCE="$BLOB_PATH/default/plymouth"
|
||||
THEME_DIR="/usr/share/plymouth/themes/blob"
|
||||
PLYMOUTH_LOGO="$THEME_DIR/logo.png"
|
||||
MKINITCPIO_DROPIN="/etc/mkinitcpio.conf.d/blob-plymouth.conf"
|
||||
|
||||
log() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; }
|
||||
info() { printf ' %s\n' "$*"; }
|
||||
@@ -43,17 +49,24 @@ apply_splash() {
|
||||
fi
|
||||
|
||||
[ -f "$image_path" ] || die "File '$image_path' does not exist."
|
||||
[ -d "$(dirname "$PLYMOUTH_LOGO")" ] || die "Plymouth blob theme is not installed."
|
||||
command -v plymouth-set-default-theme >/dev/null \
|
||||
|| die "The 'plymouth' package is not installed. Install it with: sudo pacman -S plymouth"
|
||||
|
||||
log "Applying boot splash image: $image_path"
|
||||
require_sudo
|
||||
|
||||
sudo cp "$image_path" "$PLYMOUTH_LOGO"
|
||||
sudo chmod 644 "$PLYMOUTH_LOGO"
|
||||
install_theme "$image_path"
|
||||
install_initramfs_hook
|
||||
enable_plymouth_units
|
||||
|
||||
log "Setting the default Plymouth theme"
|
||||
sudo plymouth-set-default-theme blob
|
||||
|
||||
log "Rebuilding initramfs"
|
||||
rebuild_initramfs
|
||||
|
||||
report_kernel_cmdline
|
||||
|
||||
# Under GRUB the menu references the initramfs by path, so it only needs
|
||||
# regenerating if the file set changed - but it is cheap and keeps the menu
|
||||
# honest after a kernel or os-prober change.
|
||||
@@ -65,6 +78,80 @@ apply_splash() {
|
||||
ok "Boot splash successfully updated!"
|
||||
}
|
||||
|
||||
install_theme() {
|
||||
local image_path=$1
|
||||
|
||||
[ -d "$THEME_SOURCE" ] || die "Theme source '$THEME_SOURCE' is missing from the checkout."
|
||||
|
||||
sudo install -d -m 0755 "$THEME_DIR"
|
||||
sudo install -m 0644 "$THEME_SOURCE/blob.plymouth" "$THEME_DIR/blob.plymouth"
|
||||
sudo install -m 0644 "$THEME_SOURCE/blob.script" "$THEME_DIR/blob.script"
|
||||
sudo install -m 0644 "$image_path" "$PLYMOUTH_LOGO"
|
||||
info "installed the blob theme to $THEME_DIR"
|
||||
}
|
||||
|
||||
# A drop-in rather than an edit of HOOKS: mkinitcpio.conf.d is sourced after
|
||||
# the main config, so the hook can be inserted without rewriting a line the
|
||||
# user or another tool owns. plymouth has to come after udev, which starts the
|
||||
# devices it draws on.
|
||||
install_initramfs_hook() {
|
||||
sudo install -d -m 0755 "$(dirname "$MKINITCPIO_DROPIN")"
|
||||
sudo tee "$MKINITCPIO_DROPIN" >/dev/null <<'DROPIN'
|
||||
# Written by blob-boot. Adds the plymouth hook after udev, once.
|
||||
if [[ " ${HOOKS[*]} " != *" plymouth "* ]]; then
|
||||
_hooks=()
|
||||
for _hook in "${HOOKS[@]}"; do
|
||||
_hooks+=("$_hook")
|
||||
[[ $_hook == "udev" || $_hook == "systemd" ]] && _hooks+=("plymouth")
|
||||
done
|
||||
HOOKS=("${_hooks[@]}")
|
||||
unset _hooks _hook
|
||||
fi
|
||||
DROPIN
|
||||
info "wrote $MKINITCPIO_DROPIN"
|
||||
}
|
||||
|
||||
# install.sh masks these so an unconfigured plymouth cannot hold the console and
|
||||
# leave the machine with no greeter and no TTY. A configured one needs them: if
|
||||
# plymouth-quit-wait stays masked the splash never hands the display back.
|
||||
enable_plymouth_units() {
|
||||
local unit
|
||||
for unit in plymouth-start.service plymouth-quit-wait.service; do
|
||||
if [ "$(systemctl is-enabled "$unit" 2>/dev/null)" = "masked" ]; then
|
||||
sudo systemctl unmask "$unit" >/dev/null
|
||||
info "unmasked $unit"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# plymouth only draws when the kernel was told to be quiet and to splash. The
|
||||
# bootloader owns that line, so report rather than rewrite anything but GRUB's.
|
||||
report_kernel_cmdline() {
|
||||
local params="quiet splash vt.global_cursor_default=0"
|
||||
|
||||
if grep -qw splash /proc/cmdline; then
|
||||
info "kernel command line already carries 'splash'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "One step left: the kernel command line"
|
||||
if using_grub; then
|
||||
warn "GRUB is in charge. Add to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub:"
|
||||
warn " $params"
|
||||
warn "then: sudo grub-mkconfig -o /boot/grub/grub.cfg"
|
||||
elif [ -f /boot/limine.conf ]; then
|
||||
warn "Limine is in charge. Add to the cmdline in /boot/limine.conf:"
|
||||
warn " $params"
|
||||
elif [ -d /boot/loader/entries ]; then
|
||||
warn "systemd-boot is in charge. Add to the options line in /boot/loader/entries/*.conf:"
|
||||
warn " $params"
|
||||
else
|
||||
warn "Add these to your bootloader's kernel command line:"
|
||||
warn " $params"
|
||||
fi
|
||||
warn "Without them the splash is drawn over by kernel messages."
|
||||
}
|
||||
|
||||
# Call mkinitcpio directly: limine-mkinitcpio-hook installs a wrapper at
|
||||
# /usr/local/bin/mkinitcpio that stops to ask whether to rebuild Limine
|
||||
# entries, which is useless in a script. While Limine is still the active
|
||||
@@ -82,6 +169,26 @@ using_grub() {
|
||||
[ -f /boot/grub/grub.cfg ] && [ -f /boot/EFI/GRUB/grubx64.efi ]
|
||||
}
|
||||
|
||||
# Everything Limine-specific below is skipped on a machine that never had it:
|
||||
# reclaiming its space, removing its hooks, and rewriting the mkinitcpio preset
|
||||
# its hooks needed. Rewriting that preset elsewhere would drop the fallback
|
||||
# initramfs for no reason.
|
||||
limine_present() {
|
||||
pacman -Qq limine &>/dev/null || [ -f /boot/limine.conf ] || [ -d /boot/EFI/limine ]
|
||||
}
|
||||
|
||||
install_grub_packages() {
|
||||
local missing=()
|
||||
command -v grub-install >/dev/null || missing+=(grub)
|
||||
command -v os-prober >/dev/null || missing+=(os-prober)
|
||||
|
||||
(( ${#missing[@]} == 0 )) && return 0
|
||||
|
||||
log "Installing ${missing[*]}"
|
||||
sudo pacman -S --needed --noconfirm "${missing[@]}" \
|
||||
|| die "Could not install: ${missing[*]}"
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Limine -> GRUB migration
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -92,10 +199,9 @@ migrate_to_grub() {
|
||||
|
||||
[ -d /sys/firmware/efi ] || die "Not booted in UEFI mode; this script assumes UEFI."
|
||||
mountpoint -q /boot || die "/boot is not mounted."
|
||||
command -v grub-install >/dev/null || die "The 'grub' package is not installed."
|
||||
command -v os-prober >/dev/null || die "The 'os-prober' package is not installed."
|
||||
|
||||
require_sudo
|
||||
install_grub_packages
|
||||
|
||||
local machine_id esp_uuid backup
|
||||
machine_id=$(</etc/machine-id)
|
||||
@@ -111,8 +217,14 @@ migrate_to_grub() {
|
||||
sudo efibootmgr -v | sudo tee "$backup/efibootmgr-before.txt" >/dev/null 2>&1 || true
|
||||
info "saved."
|
||||
|
||||
reclaim_esp_space "$machine_id" "$backup"
|
||||
restore_standard_initramfs
|
||||
if limine_present; then
|
||||
reclaim_esp_space "$machine_id" "$backup"
|
||||
restore_standard_initramfs
|
||||
else
|
||||
info "Limine is not installed; leaving the initramfs layout alone"
|
||||
log "Building the initramfs"
|
||||
rebuild_initramfs
|
||||
fi
|
||||
configure_grub_scripts
|
||||
install_grub "$esp_uuid"
|
||||
fix_failing_boot_units
|
||||
@@ -529,6 +641,12 @@ set_boot_order() {
|
||||
|
||||
# Delete every trace of Limine and hand its EFI fallback path to GRUB.
|
||||
purge_limine() {
|
||||
if ! limine_present; then
|
||||
log "Claiming the removable EFI fallback path for GRUB"
|
||||
sudo grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable --recheck
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Removing Limine"
|
||||
|
||||
drop_limine_pkgs limine limine-mkinitcpio-hook limine-snapper-sync
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Detect whether a DRM device with an accelerated driver is present.
|
||||
|
||||
drm_path="${BLOB_DRM_PATH:-/sys/class/drm}"
|
||||
|
||||
accelerated_drivers=(
|
||||
amdgpu
|
||||
i915
|
||||
nouveau
|
||||
nvidia
|
||||
nvidia-drm
|
||||
radeon
|
||||
xe
|
||||
)
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
for card in "$drm_path"/card[0-9]*; do
|
||||
[[ -L $card/device/driver ]] || continue
|
||||
driver=$(basename "$(readlink -f "$card/device/driver")")
|
||||
|
||||
for accelerated in "${accelerated_drivers[@]}"; do
|
||||
[[ $driver == "$accelerated" ]] && exit 0
|
||||
done
|
||||
done
|
||||
|
||||
exit 1
|
||||
@@ -12,9 +12,11 @@ LOGO_FILE="$HOME/.config/blob/branding/about.txt"
|
||||
FIT_FILE="$HOME/.local/state/blob/windows/about.fit"
|
||||
|
||||
# A user-level fastfetch config can relocate or restyle the logo in ways this
|
||||
# measurement cannot see, so leave sizing to the float rule in that case.
|
||||
# measurement cannot see, so leave sizing to the float rule in that case. Blob's
|
||||
# own config is the symlink the installer makes into the current theme, and the
|
||||
# measurements below are written against it.
|
||||
custom_fastfetch_config() {
|
||||
[[ -f $HOME/.config/fastfetch/config.jsonc ]]
|
||||
[[ -f $HOME/.config/fastfetch/config.jsonc && ! -L $HOME/.config/fastfetch/config.jsonc ]]
|
||||
}
|
||||
|
||||
# wc -L counts display columns only in a UTF-8 locale. A session that never set
|
||||
|
||||
@@ -10,4 +10,4 @@ source blob-restart-gum
|
||||
cmd="$*"
|
||||
presentation_script="blob-show-logo; $cmd; if (( \$? != 130 )); then blob-show-done; fi"
|
||||
|
||||
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.blob.terminal --title=Blob -e bash -c "$presentation_script"
|
||||
exec setsid uwsm-app -- blob-terminal-exec --app-id=org.blob.terminal --title=Blob -e bash -c "$presentation_script"
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
# blob:summary=Launch the Blob screensaver in the default terminal on the system with the correct font configuration.
|
||||
|
||||
if blob-cmd-missing ttfx; then
|
||||
if ! blob-screensaver-effect >/dev/null; then
|
||||
blob-notify-send -g ✋ "Screensaver needs ttfx or tte" "Install python-terminaltexteffects."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -15,7 +16,7 @@ if blob-toggle-enabled screensaver-off && [[ $1 != "force" ]]; then
|
||||
fi
|
||||
|
||||
focused=$(blob-hypr-monitor-focused)
|
||||
terminal=$(xdg-terminal-exec --print-id)
|
||||
terminal=$(blob-terminal-exec --print-id)
|
||||
|
||||
case $terminal in
|
||||
*Alacritty* | *ghostty* | *foot* | *kitty*) ;;
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Launch the default terminal
|
||||
# blob:args=[command [args...]]
|
||||
|
||||
exec setsid uwsm-app -- blob-terminal-exec "$@"
|
||||
+1
-1
@@ -10,4 +10,4 @@ else
|
||||
APP_ID="org.blob.$(basename $1)"
|
||||
fi
|
||||
|
||||
exec setsid uwsm-app -- xdg-terminal-exec --app-id=$APP_ID -e "$1" "${@:2}"
|
||||
exec setsid uwsm-app -- blob-terminal-exec --app-id=$APP_ID -e "$1" "${@:2}"
|
||||
|
||||
@@ -67,7 +67,7 @@ desktop_name="${desktop_file##*/}"
|
||||
desktop_name="${desktop_name%.desktop}"
|
||||
exec_line="$(sed -n 's/^Exec=//p' "$desktop_file" | head -1)"
|
||||
|
||||
if [[ $exec_line =~ (^|[[:space:]])(\$TERMINAL|xdg-terminal-exec)[[:space:]].*-e([[:space:]]|$) ]]; then
|
||||
if [[ $exec_line =~ (^|[[:space:]])(\$TERMINAL|xdg-terminal-exec|blob-terminal-exec)[[:space:]].*-e([[:space:]]|$) ]]; then
|
||||
BLOB_REMOVE_NOTIFY=false blob-tui-remove "$desktop_name"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
+28
-5
@@ -2,13 +2,37 @@
|
||||
|
||||
# blob:summary=Run the Blob screensaver using random effects from TTE.
|
||||
|
||||
effect_command=$(blob-screensaver-effect) || exit 1
|
||||
|
||||
effect_args() {
|
||||
local input="$HOME/.config/blob/branding/screensaver.txt"
|
||||
|
||||
if [[ $effect_command == "ttfx" ]]; then
|
||||
printf '%s\n' -i "$input" --frame-rate 120 --canvas-width 0 --canvas-height 0 \
|
||||
--reuse-canvas --anchor-canvas c --anchor-text c --random-effect --no-eol \
|
||||
--no-restore-cursor
|
||||
return 0
|
||||
fi
|
||||
|
||||
local effects=(
|
||||
beams binarypath blackhole bouncyballs bubbles burn colorshift crumble
|
||||
decrypt errorcorrect expand fireworks middleout orbittingvolley overflow
|
||||
pour print rain randomsequence rings scattered slice slide spotlights
|
||||
spray swarm sweep synthgrid unstable vhstape waves wipe
|
||||
)
|
||||
|
||||
printf '%s\n' -i "$input" --frame-rate 120 --canvas-width 0 --canvas-height 0 \
|
||||
--anchor-canvas c --anchor-text c \
|
||||
"${effects[RANDOM % ${#effects[@]}]}"
|
||||
}
|
||||
|
||||
screensaver_in_focus() {
|
||||
hyprctl activewindow -j | jq -e '.class == "org.blob.screensaver"' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
exit_screensaver() {
|
||||
hyprctl eval 'hl.config({ cursor = { invisible = false } })' &>/dev/null || hyprctl keyword cursor:invisible false &>/dev/null || true
|
||||
pkill -x ttfx 2>/dev/null
|
||||
pkill -x "$effect_command" 2>/dev/null
|
||||
pkill -f '[o]rg.blob.screensaver' 2>/dev/null
|
||||
exit 0
|
||||
}
|
||||
@@ -36,11 +60,10 @@ wait_for_terminal_resize() {
|
||||
wait_for_terminal_resize
|
||||
|
||||
while true; do
|
||||
ttfx -i ~/.config/blob/branding/screensaver.txt \
|
||||
--frame-rate 120 --canvas-width 0 --canvas-height 0 --reuse-canvas --anchor-canvas c --anchor-text c\
|
||||
--random-effect --no-eol --no-restore-cursor &
|
||||
mapfile -t effect_arguments < <(effect_args)
|
||||
"$effect_command" "${effect_arguments[@]}" &
|
||||
|
||||
while pgrep -t "${tty#/dev/}" -x ttfx >/dev/null; do
|
||||
while pgrep -t "${tty#/dev/}" -x "$effect_command" >/dev/null; do
|
||||
if read -n1 -t 1 || ! screensaver_in_focus; then
|
||||
exit_screensaver
|
||||
fi
|
||||
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Print the terminal text effects command the screensaver can use
|
||||
# blob:hidden=true
|
||||
|
||||
for candidate in ttfx tte; do
|
||||
if command -v "$candidate" &>/dev/null; then
|
||||
printf '%s\n' "$candidate"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
exit 1
|
||||
@@ -20,7 +20,8 @@ if pgrep -x "1password" >/dev/null && blob-cmd-present 1password; then
|
||||
fi
|
||||
|
||||
# Avoid running screensaver when locked
|
||||
pkill -x ttfx 2>/dev/null || true
|
||||
# ttfx handles SIGTERM asynchronously, so keep its terminal alive until it exits.
|
||||
timeout 1s pidwait -x ttfx 2>/dev/null || true
|
||||
effect_command=$(blob-screensaver-effect 2>/dev/null) && {
|
||||
pkill -x "$effect_command" 2>/dev/null || true
|
||||
timeout 1s pidwait -x "$effect_command" 2>/dev/null || true
|
||||
}
|
||||
pkill -f '[o]rg.blob.screensaver' 2>/dev/null || true
|
||||
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Run a command in the default terminal
|
||||
# blob:args=[--print-id] [--app-id=<app-id>] [--title=<title>] [-e] <command> [args...]
|
||||
# blob:hidden=true
|
||||
|
||||
terminal=""
|
||||
for candidate in xdg-terminal-exec foot kitty; do
|
||||
if command -v "$candidate" &>/dev/null; then
|
||||
terminal=$candidate
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $terminal ]]; then
|
||||
blob-notify-send "No terminal installed" "Install xdg-terminal-exec, foot or kitty."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $terminal == "xdg-terminal-exec" ]]; then
|
||||
exec xdg-terminal-exec "$@"
|
||||
fi
|
||||
|
||||
app_id=""
|
||||
title=""
|
||||
command_args=()
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--print-id)
|
||||
printf '%s\n' "$terminal"
|
||||
exit 0
|
||||
;;
|
||||
--app-id=*)
|
||||
app_id=${1#--app-id=}
|
||||
shift
|
||||
;;
|
||||
--title=*)
|
||||
title=${1#--title=}
|
||||
shift
|
||||
;;
|
||||
-e)
|
||||
shift
|
||||
command_args+=("$@")
|
||||
break
|
||||
;;
|
||||
*)
|
||||
command_args+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
launch=("$terminal")
|
||||
|
||||
case "$terminal" in
|
||||
foot)
|
||||
[[ -n $app_id ]] && launch+=("--app-id=$app_id")
|
||||
;;
|
||||
kitty)
|
||||
[[ -n $app_id ]] && launch+=("--class=$app_id")
|
||||
;;
|
||||
esac
|
||||
|
||||
[[ -n $title ]] && launch+=("--title=$title")
|
||||
|
||||
exec "${launch[@]}" "${command_args[@]}"
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Apply the current Blob theme's light or dark preference to GTK apps
|
||||
# blob:hidden=true
|
||||
|
||||
blob-cmd-present gsettings || exit 0
|
||||
|
||||
colors_toml=$HOME/.local/state/blob/current/theme/colors.toml
|
||||
[[ -f $colors_toml ]] || exit 0
|
||||
|
||||
mode=$(blob-theme-color --file "$colors_toml" mode)
|
||||
|
||||
if [[ $mode == "light" ]]; then
|
||||
color_scheme="prefer-light"
|
||||
gtk_theme="Adwaita"
|
||||
else
|
||||
color_scheme="prefer-dark"
|
||||
gtk_theme="Adwaita-dark"
|
||||
fi
|
||||
|
||||
gsettings set org.gnome.desktop.interface color-scheme "$color_scheme" 2>/dev/null || true
|
||||
gsettings set org.gnome.desktop.interface gtk-theme "$gtk_theme" 2>/dev/null || true
|
||||
+3
-3
@@ -21,7 +21,7 @@ current_mode() {
|
||||
}
|
||||
|
||||
list_theme_rows() {
|
||||
printf 'Dynamic (from wallpaper)\tblob-dynamic\n'
|
||||
printf '\tDynamic (from wallpaper)\tblob-dynamic\n'
|
||||
|
||||
{
|
||||
find "$user_themes_dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null
|
||||
@@ -30,8 +30,8 @@ list_theme_rows() {
|
||||
[ -f "$theme_dir/colors.toml" ] || continue
|
||||
slug=$(basename "$theme_dir")
|
||||
[ "$slug" = "blob-dynamic" ] && continue
|
||||
printf '%s\t%s\n' "$(prettify "$slug")" "$slug"
|
||||
done | sort -u -t$'\t' -k2,2
|
||||
printf '\t%s\t%s\n' "$(prettify "$slug")" "$slug"
|
||||
done | sort -u -t$'\t' -k3,3
|
||||
}
|
||||
|
||||
print_current_colors() {
|
||||
|
||||
+1
-1
@@ -108,7 +108,6 @@ set_theme_background() {
|
||||
local new_background new_background_snapshot
|
||||
|
||||
if ! choose_theme_background; then
|
||||
blob-notify-send "No background was found for theme" -t 2000
|
||||
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
||||
return
|
||||
fi
|
||||
@@ -321,6 +320,7 @@ post_theme_commands=(
|
||||
blob-restart-btop
|
||||
blob-theme-foot
|
||||
blob-theme-browser
|
||||
blob-theme-gtk
|
||||
)
|
||||
|
||||
if [[ $THEME_HEADLESS != "1" ]]; then
|
||||
|
||||
@@ -203,6 +203,7 @@ add_template_value() {
|
||||
if [[ $value =~ ^#[0-9A-Fa-f]{6}$ ]]; then
|
||||
rgb=$(hex_to_rgb "$value")
|
||||
printf 's|{{ %s_rgb }}|%s|g\n' "$key" "$rgb" >>"$sed_script"
|
||||
printf 's|{{ %s_ansi }}|38;2;%s|g\n' "$key" "${rgb//,/;}" >>"$sed_script"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ cat >"$DESKTOP_FILE" <<EOF
|
||||
Version=1.0
|
||||
Name=$APP_NAME
|
||||
Comment=$APP_NAME
|
||||
Exec=xdg-terminal-exec --app-id=$APP_CLASS -e $APP_EXEC
|
||||
Exec=blob-terminal-exec --app-id=$APP_CLASS -e $APP_EXEC
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=$ICON_VALUE
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||
if (( $# == 0 )); then
|
||||
# Find all TUIs
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -qE '^Exec=.*(\$TERMINAL|xdg-terminal-exec).*-e' "$file"; then
|
||||
if grep -qE '^Exec=.*(\$TERMINAL|xdg-terminal-exec|blob-terminal-exec).*-e' "$file"; then
|
||||
TUIS+=("$(basename "${file%.desktop}")")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
||||
|
||||
@@ -12,7 +12,7 @@ echo "Scanning for TUIs in $APP_DIR..."
|
||||
|
||||
tui_desktop_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q "Exec=xdg-terminal-exec --app-id=TUI\." "$file" 2>/dev/null; then
|
||||
if grep -qE "Exec=(xdg|blob)-terminal-exec --app-id=TUI\." "$file" 2>/dev/null; then
|
||||
tui_desktop_files+=("$file")
|
||||
fi
|
||||
done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null)
|
||||
|
||||
@@ -37,6 +37,11 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v wal >/dev/null 2>&1; then
|
||||
echo "Error: 'wal' is not installed. Install it with: yay -S python-pywal16"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Extracting colors using Pywal..."
|
||||
wal -i "$IMAGE_PATH" -n -q 2> >(grep -v "deprecated in IMv7" >&2)
|
||||
|
||||
|
||||
+25
-26
@@ -1,26 +1,25 @@
|
||||
@@@@@@@@@@@@
|
||||
@@@@ @@@@
|
||||
@@@ @@@
|
||||
@@@ @@@
|
||||
@@@ @@
|
||||
@@ @@ @@ @@
|
||||
@ @@ @@ @@ @@ @@
|
||||
@ @@ ##### @ @ @ @@
|
||||
@ @ ##### @ @ ##### @ @@
|
||||
@@ @ ### @ @ ###### @ @@
|
||||
@ @ @ @ ######@@ @@
|
||||
@@ @@@ @@@ @@ @@@ @
|
||||
@@ @@
|
||||
@@ @@
|
||||
@@ @
|
||||
@ @@
|
||||
@@ @@
|
||||
@@ @@
|
||||
@ @@
|
||||
@@ @@
|
||||
@@ @@
|
||||
@@@ @@
|
||||
@@@ @@@
|
||||
@@@ @@@
|
||||
@@@@ @@@@
|
||||
@@@@@@@@@@@@
|
||||
@@ @@ @@
|
||||
@@ @@ @@
|
||||
@@ @@ @@
|
||||
@@ @@ @@
|
||||
@@ @@ @@
|
||||
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@ @@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@ @@@@@@
|
||||
@@ @@ @@
|
||||
@@ @@ @@
|
||||
@@ @@ @@
|
||||
@@ @@ @@
|
||||
@@ @@@@@@@
|
||||
@@ @@
|
||||
@@ @@
|
||||
@@ @@
|
||||
@@ @@
|
||||
@@@@@@@@@@@@@@@@
|
||||
|
||||
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@ @@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<rect x="8.6" y="1.6" width="1.3" height="4.6" rx="0.65" fill="#000000" transform="rotate(-12 9.25 3.9)"/>
|
||||
<rect x="12.4" y="2.4" width="1.3" height="4.6" rx="0.65" fill="#000000" transform="rotate(12 13.05 4.7)"/>
|
||||
<path d="M17.2 11 a3.4 3.4 0 0 1 0 6 v-1.7 a1.7 1.7 0 0 0 0 -2.6 z" fill="#000000"/>
|
||||
<path d="M4 9 h14 l-2.5 9.2 a1.6 1.6 0 0 1 -1.55 1.2 h-5.9 a1.6 1.6 0 0 1 -1.55 -1.2 z" fill="#000000"/>
|
||||
<rect x="3" y="20.4" width="18" height="2" rx="1" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 612 B |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<match target="pattern">
|
||||
<test name="family" qual="any">
|
||||
<string>monospace</string>
|
||||
</test>
|
||||
<edit name="family" mode="prepend_first" binding="strong">
|
||||
<string>JetBrainsMono Nerd Font</string>
|
||||
</edit>
|
||||
</match>
|
||||
</fontconfig>
|
||||
@@ -1,32 +0,0 @@
|
||||
add_newline = true
|
||||
command_timeout = 200
|
||||
format = "[$directory$git_branch$git_status]($style)$character"
|
||||
|
||||
[character]
|
||||
error_symbol = "[✗](bold cyan)"
|
||||
success_symbol = "[❯](bold cyan)"
|
||||
|
||||
[directory]
|
||||
truncation_length = 2
|
||||
truncation_symbol = "…/"
|
||||
repo_root_style = "bold cyan"
|
||||
repo_root_format = "[$repo_root]($repo_root_style)[$path]($style)[$read_only]($read_only_style) "
|
||||
|
||||
[git_branch]
|
||||
format = "[$branch]($style) "
|
||||
style = "italic cyan"
|
||||
|
||||
[git_status]
|
||||
format = '[$all_status]($style)'
|
||||
style = "cyan"
|
||||
ahead = "⇡${count} "
|
||||
diverged = "⇕⇡${ahead_count}⇣${behind_count} "
|
||||
behind = "⇣${count} "
|
||||
conflicted = " "
|
||||
up_to_date = " "
|
||||
untracked = "? "
|
||||
modified = " "
|
||||
stashed = ""
|
||||
staged = ""
|
||||
renamed = ""
|
||||
deleted = ""
|
||||
@@ -254,8 +254,8 @@
|
||||
"setup.direct-boot": {"icon":"","label":"Direct Boot","action":"blob-launch-floating blob-setup-direct-boot"},
|
||||
|
||||
// Install
|
||||
"install.package": {"icon":"","label":"Package","action":"xdg-terminal-exec --app-id=org.blob.terminal blob-pkg-install"},
|
||||
"install.aur": {"icon":"","label":"AUR","action":"xdg-terminal-exec --app-id=org.blob.terminal blob-pkg-aur-install"},
|
||||
"install.package": {"icon":"","label":"Package","action":"blob-terminal-exec --app-id=org.blob.terminal blob-pkg-install"},
|
||||
"install.aur": {"icon":"","label":"AUR","action":"blob-terminal-exec --app-id=org.blob.terminal blob-pkg-aur-install"},
|
||||
"install.tui": {"icon":"","label":"TUI","action":"blob-launch-floating blob-tui-install"},
|
||||
"install.style": {"icon":"","label":"Style"},
|
||||
"install.style.theme": {"icon":"","label":"Theme","action":"blob-launch-floating blob-theme-install"},
|
||||
@@ -269,8 +269,8 @@
|
||||
"install.style.font.iosevka": {"icon":"","label":"Iosevka","action":"blob-install-font Iosevka ttf-iosevka-nerd 'Iosevka Nerd Font Mono'"},
|
||||
|
||||
// Remove
|
||||
"remove.package": {"icon":"","label":"Package","action":"xdg-terminal-exec --app-id=org.blob.terminal blob-pkg-remove"},
|
||||
"remove.tui": {"icon":"","label":"TUI","when":"grep -qE '^Exec=.*(\\$TERMINAL|xdg-terminal-exec).*-e' $HOME/.local/share/applications/*.desktop","action":"blob-tui-remove"},
|
||||
"remove.package": {"icon":"","label":"Package","action":"blob-terminal-exec --app-id=org.blob.terminal blob-pkg-remove"},
|
||||
"remove.tui": {"icon":"","label":"TUI","when":"grep -qE '^Exec=.*(\\$TERMINAL|xdg-terminal-exec|blob-terminal-exec).*-e' $HOME/.local/share/applications/*.desktop","action":"blob-tui-remove"},
|
||||
"remove.theme": {"icon":"","label":"Theme","action":"blob-theme-remove"},
|
||||
|
||||
// Update
|
||||
|
||||
@@ -37,6 +37,7 @@ hl.env("PATH", table.concat(kept, ":"))
|
||||
|
||||
-- Hardware-specific environment.
|
||||
require("default.hypr.nvidia")
|
||||
require("default.hypr.renderer")
|
||||
|
||||
hl.config({
|
||||
xwayland = {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
local paths = require("default.hypr.paths")
|
||||
|
||||
local accelerated = paths.blob_path .. "/bin/blob-hw-gpu-accelerated"
|
||||
|
||||
if not o.shell_succeeds(o.shell_quote(accelerated)) then
|
||||
hl.env("LIBGL_ALWAYS_SOFTWARE", "1")
|
||||
hl.env("WLR_NO_HARDWARE_CURSORS", "1")
|
||||
|
||||
hl.config({
|
||||
cursor = {
|
||||
no_hardware_cursors = true,
|
||||
},
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
#%PAM-1.0
|
||||
auth include system-auth
|
||||
@@ -0,0 +1,8 @@
|
||||
[Plymouth Theme]
|
||||
Name=Blob
|
||||
Description=Blob boot splash
|
||||
ModuleName=script
|
||||
|
||||
[script]
|
||||
ImageDir=/usr/share/plymouth/themes/blob
|
||||
ScriptFile=/usr/share/plymouth/themes/blob/blob.script
|
||||
@@ -0,0 +1,70 @@
|
||||
Window.SetBackgroundTopColor(0.059, 0.063, 0.071);
|
||||
Window.SetBackgroundBottomColor(0.059, 0.063, 0.071);
|
||||
|
||||
screen.width = Window.GetWidth();
|
||||
screen.height = Window.GetHeight();
|
||||
|
||||
logo.image = Image("logo.png");
|
||||
logo.width = logo.image.GetWidth();
|
||||
logo.height = logo.image.GetHeight();
|
||||
|
||||
scale_x = screen.width / logo.width;
|
||||
scale_y = screen.height / logo.height;
|
||||
|
||||
scale = scale_x;
|
||||
if (scale_y < scale_x) {
|
||||
scale = scale_y;
|
||||
}
|
||||
|
||||
if (scale != 1) {
|
||||
logo.image = logo.image.Scale(logo.width * scale, logo.height * scale);
|
||||
logo.width = logo.image.GetWidth();
|
||||
logo.height = logo.image.GetHeight();
|
||||
}
|
||||
|
||||
logo.sprite = Sprite(logo.image);
|
||||
logo.sprite.SetX((screen.width - logo.width) / 2);
|
||||
logo.sprite.SetY((screen.height - logo.height) / 2);
|
||||
logo.sprite.SetZ(0);
|
||||
|
||||
prompt.sprite = Sprite();
|
||||
prompt.sprite.SetZ(10);
|
||||
message.sprite = Sprite();
|
||||
message.sprite.SetZ(10);
|
||||
|
||||
fun draw_prompt(text) {
|
||||
if (text == "") {
|
||||
prompt.sprite.SetImage(Image.Text(" ", 1, 1, 1));
|
||||
return;
|
||||
}
|
||||
|
||||
prompt.image = Image.Text(text, 0.8, 0.82, 0.85);
|
||||
prompt.sprite.SetImage(prompt.image);
|
||||
prompt.sprite.SetX((screen.width - prompt.image.GetWidth()) / 2);
|
||||
prompt.sprite.SetY(screen.height * 0.82);
|
||||
}
|
||||
|
||||
fun display_password_callback(prompt_text, bullets) {
|
||||
dots = "";
|
||||
index = 0;
|
||||
while (index < bullets) {
|
||||
dots += "*";
|
||||
index++;
|
||||
}
|
||||
draw_prompt(prompt_text + " " + dots);
|
||||
}
|
||||
|
||||
fun display_normal_callback() {
|
||||
draw_prompt("");
|
||||
}
|
||||
|
||||
fun message_callback(text) {
|
||||
message.image = Image.Text(text, 0.8, 0.82, 0.85);
|
||||
message.sprite.SetImage(message.image);
|
||||
message.sprite.SetX((screen.width - message.image.GetWidth()) / 2);
|
||||
message.sprite.SetY(screen.height * 0.9);
|
||||
}
|
||||
|
||||
Plymouth.SetDisplayPasswordFunction(display_password_callback);
|
||||
Plymouth.SetDisplayNormalFunction(display_normal_callback);
|
||||
Plymouth.SetMessageFunction(message_callback);
|
||||
@@ -1,6 +1,31 @@
|
||||
-- Hyprland config for the SDDM Wayland greeter. SDDM starts the greeter itself
|
||||
-- once the compositor is up, so nothing here launches anything.
|
||||
-- The greeter runs as SDDM's own user, with no BLOB_PATH and no read access to
|
||||
-- the checkout, so it calls the copy install.sh puts in /usr/local/bin rather
|
||||
-- than loading the Lua modules the session uses. A detector that is not there
|
||||
-- yet reads as no acceleration, which is the fallback that always draws.
|
||||
local function gpu_is_accelerated()
|
||||
local pipe = io.popen("/usr/local/bin/blob-hw-gpu-accelerated >/dev/null 2>&1 && echo OK")
|
||||
if not pipe then
|
||||
return false
|
||||
end
|
||||
|
||||
local output = pipe:read("*a") or ""
|
||||
pipe:close()
|
||||
|
||||
return output:match("OK") ~= nil
|
||||
end
|
||||
|
||||
if not gpu_is_accelerated() then
|
||||
hl.env("LIBGL_ALWAYS_SOFTWARE", "1")
|
||||
hl.env("WLR_NO_HARDWARE_CURSORS", "1")
|
||||
end
|
||||
|
||||
hl.config({
|
||||
cursor = {
|
||||
no_hardware_cursors = true,
|
||||
},
|
||||
|
||||
misc = {
|
||||
disable_hyprland_logo = true,
|
||||
disable_splash_rendering = true,
|
||||
|
||||
@@ -14,7 +14,7 @@ theme[hi_fg]="{{ accent }}"
|
||||
theme[selected_bg]="{{ selection }}"
|
||||
|
||||
# Foreground color of selected item in processes box
|
||||
theme[selected_fg]="{{ accent }}"
|
||||
theme[selected_fg]="{{ selection_foreground }}"
|
||||
|
||||
# Color of inactive/disabled text
|
||||
theme[inactive_fg]="{{ muted }}"
|
||||
@@ -23,7 +23,7 @@ theme[inactive_fg]="{{ muted }}"
|
||||
theme[graph_text]="{{ light_foreground }}"
|
||||
|
||||
# Background color of the percentage meters
|
||||
theme[meter_bg]="{{ selection }}"
|
||||
theme[meter_bg]="{{ lighter_background }}"
|
||||
|
||||
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||
theme[proc_misc]="{{ light_foreground }}"
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
"type": "file",
|
||||
"source": "~/.config/blob/branding/about.txt",
|
||||
"padding": {
|
||||
"top": 2,
|
||||
"left": 2,
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"display": {
|
||||
"separator": " ",
|
||||
"key": {
|
||||
"width": 9
|
||||
},
|
||||
"color": {
|
||||
"title": "{{ accent_ansi }}",
|
||||
"keys": "{{ accent_ansi }}",
|
||||
"output": "{{ foreground_ansi }}"
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"type": "title",
|
||||
"format": "{user-name}@{host-name}"
|
||||
},
|
||||
"break",
|
||||
{ "type": "os", "key": "os" },
|
||||
{ "type": "kernel", "key": "kernel" },
|
||||
{ "type": "uptime", "key": "uptime" },
|
||||
{ "type": "packages", "key": "packages" },
|
||||
{ "type": "shell", "key": "shell" },
|
||||
"break",
|
||||
{ "type": "wm", "key": "wm" },
|
||||
{ "type": "terminal", "key": "term" },
|
||||
{ "type": "display", "key": "display" },
|
||||
"break",
|
||||
{ "type": "cpu", "key": "cpu" },
|
||||
{ "type": "gpu", "key": "gpu" },
|
||||
{ "type": "memory", "key": "memory" },
|
||||
{ "type": "disk", "key": "disk" },
|
||||
"break",
|
||||
{
|
||||
"type": "colors",
|
||||
"symbol": "circle",
|
||||
"paddingLeft": 9
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
@define-color window_bg_color {{ background }};
|
||||
@define-color window_fg_color {{ foreground }};
|
||||
@define-color view_bg_color {{ background }};
|
||||
@define-color view_fg_color {{ foreground }};
|
||||
@define-color headerbar_bg_color {{ lighter_background }};
|
||||
@define-color headerbar_fg_color {{ foreground }};
|
||||
@define-color headerbar_border_color {{ dark_background }};
|
||||
@define-color headerbar_backdrop_color {{ background }};
|
||||
@define-color sidebar_bg_color {{ lighter_background }};
|
||||
@define-color sidebar_fg_color {{ foreground }};
|
||||
@define-color sidebar_backdrop_color {{ background }};
|
||||
@define-color sidebar_border_color {{ dark_background }};
|
||||
@define-color secondary_sidebar_bg_color {{ lighter_background }};
|
||||
@define-color secondary_sidebar_fg_color {{ foreground }};
|
||||
@define-color secondary_sidebar_backdrop_color {{ background }};
|
||||
@define-color secondary_sidebar_border_color {{ dark_background }};
|
||||
@define-color card_bg_color {{ lighter_background }};
|
||||
@define-color card_fg_color {{ foreground }};
|
||||
@define-color dialog_bg_color {{ background }};
|
||||
@define-color dialog_fg_color {{ foreground }};
|
||||
@define-color popover_bg_color {{ lighter_background }};
|
||||
@define-color popover_fg_color {{ foreground }};
|
||||
@define-color accent_bg_color {{ accent }};
|
||||
@define-color accent_fg_color {{ background }};
|
||||
@define-color accent_color {{ accent }};
|
||||
@define-color destructive_bg_color {{ red }};
|
||||
@define-color destructive_fg_color {{ background }};
|
||||
@define-color destructive_color {{ red }};
|
||||
@define-color success_color {{ green }};
|
||||
@define-color warning_color {{ yellow }};
|
||||
@define-color error_color {{ red }};
|
||||
|
||||
@define-color theme_bg_color {{ background }};
|
||||
@define-color theme_fg_color {{ foreground }};
|
||||
@define-color theme_base_color {{ background }};
|
||||
@define-color theme_text_color {{ foreground }};
|
||||
@define-color theme_selected_bg_color {{ accent }};
|
||||
@define-color theme_selected_fg_color {{ background }};
|
||||
@define-color insensitive_bg_color {{ dark_background }};
|
||||
@define-color insensitive_fg_color {{ muted }};
|
||||
@define-color borders {{ dark_background }};
|
||||
@@ -0,0 +1,42 @@
|
||||
add_newline = true
|
||||
command_timeout = 200
|
||||
format = "$directory$git_branch$git_status$character"
|
||||
palette = "blob"
|
||||
|
||||
[palettes.blob]
|
||||
accent = "{{ accent }}"
|
||||
foreground = "{{ foreground }}"
|
||||
muted = "{{ muted }}"
|
||||
success = "{{ green }}"
|
||||
warning = "{{ yellow }}"
|
||||
error = "{{ red }}"
|
||||
|
||||
[character]
|
||||
error_symbol = "[✗](bold error)"
|
||||
success_symbol = "[❯](bold accent)"
|
||||
|
||||
[directory]
|
||||
style = "foreground"
|
||||
truncation_length = 2
|
||||
truncation_symbol = "…/"
|
||||
repo_root_style = "bold accent"
|
||||
repo_root_format = "[$repo_root]($repo_root_style)[$path]($style)[$read_only]($read_only_style) "
|
||||
|
||||
[git_branch]
|
||||
format = "[$branch]($style) "
|
||||
style = "italic accent"
|
||||
|
||||
[git_status]
|
||||
format = '[$all_status ]($style)'
|
||||
style = "warning"
|
||||
ahead = "⇡${count} "
|
||||
diverged = "⇕⇡${ahead_count}⇣${behind_count} "
|
||||
behind = "⇣${count} "
|
||||
conflicted = " "
|
||||
up_to_date = " "
|
||||
untracked = "? "
|
||||
modified = " "
|
||||
stashed = ""
|
||||
staged = ""
|
||||
renamed = ""
|
||||
deleted = ""
|
||||
@@ -206,6 +206,7 @@ of the `blob` listing.
|
||||
| `blob-hw-display` | Print the most likely display backlight device. | - |
|
||||
| `blob-hw-external` | Returns true when an external monitor is physically connected. | - |
|
||||
| `blob-hw-fingerprint` | Returns true when a fingerprint reader is present (hidden) | - |
|
||||
| `blob-hw-gpu-accelerated` | Detect whether a DRM device with an accelerated driver is present. | - |
|
||||
| `blob-hw-hybrid-gpu` | Detect whether the system has an active hybrid GPU configuration | - |
|
||||
| `blob-hw-laptop` | Returns true when running on a laptop (has a lid or laptop chassis). | - |
|
||||
| `blob-hw-laptop-closed` | Returns true when the laptop lid is closed (hidden) | - |
|
||||
@@ -295,6 +296,7 @@ of the `blob` listing.
|
||||
| `blob-launch-or-focus-webapp` | Launch or focus on a given web app identified by the window-pattern. | <window-pattern> <url-and-flags...> |
|
||||
| `blob-launch-screensaver` | Launch the Blob screensaver in the default terminal on the system with the correct font configuration. | - |
|
||||
| `blob-launch-shell` | Launch the Blob shell with its log kept in the journal (hidden) | - |
|
||||
| `blob-launch-terminal` | Launch the default terminal | [command [args...]] |
|
||||
| `blob-launch-tui` | Launch a TUI command in the default terminal with Blob styling | [--app-id=<app-id>] <command> [args...] |
|
||||
| `blob-launch-webapp` | Launch a URL as a web app in the default supported browser | <url> |
|
||||
|
||||
@@ -531,6 +533,7 @@ of the `blob` listing.
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-terminal-exec` | Run a command in the default terminal (hidden) | [--print-id] [--app-id=<app-id>] [--title=<title>] [-e] <command> [args...] |
|
||||
| `blob-theme-browser` | Apply the current theme color to Chromium, Chrome, Edge, and Brave (hidden) | - |
|
||||
| `blob-theme-browser-policy` | Write the current theme color into the browser policy directories (hidden) | <rrggbb> |
|
||||
| `blob-theme-color` | Resolve semantic colors from an Blob theme colors.toml (hidden) | [--file <colors.toml>] (--all \| --raw \| <key> [fallback]) |
|
||||
|
||||
+134
-11
@@ -13,7 +13,7 @@ with `--check`.
|
||||
| `install/helpers/as-root.sh` | `as_root`, a no-op when already root |
|
||||
| `install/steps/directories.sh` | the directories the shell and scripts expect |
|
||||
| `install/steps/packages.sh` | repo and AUR packages, and the yay bootstrap |
|
||||
| `install/steps/config.sh` | the `BLOB_PATH` symlink and everything under `~/.config` |
|
||||
| `install/steps/config.sh` | the `BLOB_PATH` symlink, everything under `~/.config`, and the login shell profile |
|
||||
| `install/steps/services.sh` | system services, systemd drop-ins, user units |
|
||||
| `install/steps/theme.sh` | the wallpaper link, the first theme, the first background |
|
||||
| `install/steps/login.sh` | SDDM, the session entry, and the first launch |
|
||||
@@ -30,9 +30,27 @@ with `--check`.
|
||||
than assuming step 1 ran.
|
||||
3. **Packages** come from `packages/blob.packages` and
|
||||
`packages/blob-aur.packages`. A machine with no AUR helper gets `yay-bin`
|
||||
built once with `makepkg`. See [packages.md](packages.md).
|
||||
built once with `makepkg`. A failed batch refreshes the databases and retries,
|
||||
then falls back to one package at a time, so a single name that no longer
|
||||
resolves cannot cost you the other hundred and the config, service and login
|
||||
steps below. See [packages.md](packages.md).
|
||||
4. **Config** is copied into `~/.config`: `hypr/`, the themed app configs,
|
||||
`blob/shell.json`, the uwsm environment, and the icon font.
|
||||
`blob/shell.json`, the uwsm environment, `fontconfig/fonts.conf`, and the
|
||||
icon font, and the font cache is refreshed. The shell draws every icon as a
|
||||
Nerd Font glyph in the `monospace` family, and on a fresh Arch install
|
||||
fontconfig resolves that to a font with no such glyphs, so the bar and its
|
||||
panels come up full of blanks. `fonts.conf` binds `monospace` to
|
||||
`JetBrainsMono Nerd Font`; `blob font set` rewrites the same file, and the
|
||||
installer then reports it as a local change and keeps your choice.
|
||||
`~/.bashrc` gets one line sourcing `session/bashrc`. The line is added once,
|
||||
a `.bak` is kept, and a `~/.bashrc` that already has it is left alone.
|
||||
`session/profile.sh` also goes to `/etc/profile.d/blob.sh`, which sources
|
||||
`session/env-bootstrap` and is what puts `blob` on `PATH` in a login shell.
|
||||
Without it `BLOB_PATH` and `$BLOB_PATH/bin` are only set by
|
||||
`~/.config/uwsm/env.d/10-blob`, which uwsm reads when the graphical session
|
||||
starts, so `blob` is not found from a TTY. A shell that was already open when
|
||||
the installer ran keeps its old `PATH`; open a new one, or
|
||||
`source ~/.local/share/blob/session/env-bootstrap`.
|
||||
5. **The first theme** is applied. `~/wallpapers` is symlinked to the checkout's
|
||||
`wallpapers/`, `flats` is applied with `BLOB_THEME_HEADLESS=1` (no shell or
|
||||
session bus exists yet), and the background symlink is pointed at the first
|
||||
@@ -42,7 +60,10 @@ with `--check`.
|
||||
`~/.config/btop/themes/current.theme` (btop's `color_theme = "current"`) and,
|
||||
only when `~/.config/nvim/lua/plugins` already exists, `theme.lua` there.
|
||||
Every step is skipped when a theme, a background, a wallpaper directory or a
|
||||
theme file of your own is already in place.
|
||||
theme file of your own is already in place. A theme counts as in place only
|
||||
when the staged theme under `~/.local/state/blob/current/theme` is there too,
|
||||
so a recorded theme name left without its palette is applied again rather
|
||||
than skipped.
|
||||
6. **User units** land in `~/.config/systemd/user` and are enabled, not started:
|
||||
they are all `WantedBy=graphical-session.target`, so they come up with the
|
||||
session. `blob-speaker-tuning.service` is left out, because
|
||||
@@ -52,18 +73,37 @@ with `--check`.
|
||||
8. **System services** are enabled: NetworkManager, resolved, bluetooth, cups,
|
||||
avahi, docker, power-profiles-daemon, and oomd.
|
||||
`NetworkManager-wait-online.service` is masked so a slow DHCP lease cannot
|
||||
hold up the login screen.
|
||||
9. **The login screen** is SDDM. Three pieces have to be in place:
|
||||
hold up the login screen, and `plymouth-start.service` and
|
||||
`plymouth-quit-wait.service` are masked because an unconfigured plymouth
|
||||
holds the console and leaves the machine with neither a greeter nor a TTY.
|
||||
`blob boot` unmasks them when it sets the splash up. See
|
||||
[packages.md](packages.md#the-boot-splash).
|
||||
9. **The login screen** is SDDM. It has to be installed first: if
|
||||
`sddm.service` is not there, the installer says whether the package is
|
||||
missing or whether systemd has simply not re-read its units yet, and carries
|
||||
on without enabling anything. A unit counts as present when `systemctl cat`
|
||||
finds it or when its file is under `/etc/systemd/system`,
|
||||
`/run/systemd/system`, `/usr/local/lib/systemd/system` or
|
||||
`/usr/lib/systemd/system` - `systemctl` goes through the service manager, so
|
||||
it reports a perfectly good unit as missing inside a chroot or right after
|
||||
pacman wrote it.
|
||||
|
||||
Three pieces have to be in place:
|
||||
|
||||
| File | Why |
|
||||
| --- | --- |
|
||||
| `/usr/local/share/wayland-sessions/blob.desktop` | the session SDDM offers |
|
||||
| `/etc/sddm.conf.d/zz-blob.conf` | Wayland greeter, run on Hyprland, stock theme, remember the last session |
|
||||
| `/etc/sddm/hyprland-greeter.lua` | the greeter's own tiny Hyprland config |
|
||||
| `/usr/local/bin/blob-hw-gpu-accelerated` | the renderer check that config runs, where SDDM's user can read it |
|
||||
| `/etc/pam.d/blob-lock-password` | the PAM stack the lock screen authenticates against |
|
||||
|
||||
The greeter config matters more than it looks: SDDM's stock Wayland greeter
|
||||
runs on `weston`, which Blob does not install, so a default SDDM would fail
|
||||
to draw anything. That greeter config has to be Lua: Hyprland dropped the old
|
||||
to draw anything. The installer checks that the `CompositorCommand` named in
|
||||
`zz-blob.conf` is actually installed and warns when it is not, because SDDM
|
||||
with a compositor command it cannot run starts, fails, and leaves the machine
|
||||
sitting at a text console with no explanation. That greeter config has to be Lua: Hyprland dropped the old
|
||||
hyprlang format, and a config it cannot parse leaves the greeter with no
|
||||
compositor and the screen black. `sddm.service` is enabled and the default
|
||||
systemd target is set to `graphical.target`, which an Arch install without a
|
||||
@@ -87,10 +127,13 @@ with `--check`.
|
||||
|
||||
`--autologin` writes `zzz-blob-autologin.conf`, the only file that sorts after
|
||||
`zz-blob.conf`.
|
||||
10. **Launch.** When the installer was run from a console and nothing graphical
|
||||
is running, it starts `sddm.service` so the desktop appears without a reboot.
|
||||
`--no-launch` prints the command instead. `--autologin` writes
|
||||
`/etc/sddm.conf.d/zzz-blob-autologin.conf` for the current user.
|
||||
10. **Launch.** `sddm`, `hyprland`, `quickshell` and `uwsm` are checked one last
|
||||
time and any that are missing are named, because that is the whole difference
|
||||
between a desktop and a console. Then, when the installer was run from a
|
||||
console and nothing graphical is running, it starts `sddm.service` so the
|
||||
desktop appears without a reboot. `--no-launch` prints the command instead.
|
||||
`--autologin` writes `/etc/sddm.conf.d/zzz-blob-autologin.conf` for the
|
||||
current user.
|
||||
|
||||
## Flags
|
||||
|
||||
@@ -102,6 +145,86 @@ with `--check`.
|
||||
| `--autologin` | skip the SDDM prompt for this user |
|
||||
| `--no-launch` | leave the login screen for the next reboot |
|
||||
|
||||
## The interactive shell
|
||||
|
||||
`session/bashrc` is sourced from `~/.bashrc` and returns immediately in a
|
||||
non-interactive shell, so it cannot disturb `scp` or `rsync`. It does three
|
||||
things, each only when the tool is installed:
|
||||
|
||||
| Line | Effect |
|
||||
| --- | --- |
|
||||
| `session/env-bootstrap` | `BLOB_PATH` and `$BLOB_PATH/bin` on `PATH`, so `blob` resolves |
|
||||
| `starship init bash` | the themed prompt, see [themes.md](themes.md#the-prompt) |
|
||||
| `zoxide init bash --cmd cd` | `cd` learns the directories you actually use |
|
||||
|
||||
`--cmd cd` replaces `cd` rather than adding a `z` command, so
|
||||
`cd blomarchy` jumps to the last matching directory you have visited from
|
||||
anywhere, while `cd ./relative/path`, `cd -` and `cd` with no argument keep
|
||||
behaving exactly as the builtin does. `cdi` picks from the matches
|
||||
interactively through `fzf`, and `zoxide query -ls` prints the database.
|
||||
|
||||
The database is built as you go: it learns a directory the first time you `cd`
|
||||
there, so the jumping is only as good as the history behind it and a fresh
|
||||
install knows nothing yet.
|
||||
|
||||
## The lock screen
|
||||
|
||||
The lock plugin watches `/etc/pam.d/blob-lock-password` and refuses to lock at
|
||||
all when it is not there - `blob system lock` returns `missing-pam` and nothing
|
||||
appears, which reads as a keybinding that does nothing. The installer writes it;
|
||||
it is one line including `system-auth`, the same stack every other Wayland
|
||||
locker on Arch authenticates against.
|
||||
|
||||
Fingerprint unlock is separate and opt-in: `blob setup-security-fingerprint`
|
||||
writes `/etc/pam.d/blob-lock-fingerprint`, and the lock only offers fingerprint
|
||||
when that file and a reader are both present.
|
||||
|
||||
## Choosing a renderer
|
||||
|
||||
Hyprland renders through Mesa, and on an adapter Mesa has no driver for it exits
|
||||
with `failed to create dri2 screen` instead of drawing. The greeter is Hyprland
|
||||
too, so that is a black screen at boot and a second one after login, which is
|
||||
how a machine with no real GPU behind its display arrives.
|
||||
|
||||
`blob-hw-gpu-accelerated` decides which way to go. It reads the kernel driver of
|
||||
each `/sys/class/drm/card*` from sysfs, not from `lspci`, which reads PCI config
|
||||
space and resumes runtime-suspended GPUs. A card driven by `amdgpu`, `i915`,
|
||||
`nouveau`, `nvidia`, `nvidia-drm`, `radeon` or `xe` is a real integrated or
|
||||
dedicated GPU and hardware rendering is left alone. Everything else - `vmwgfx`,
|
||||
`vboxvideo`, `qxl`, `bochs-drm`, `cirrus`, `simpledrm`, `vkms`, `virtio_gpu` -
|
||||
is virtual or framebuffer-only, and two settings are applied:
|
||||
|
||||
| Setting | Effect |
|
||||
| --- | --- |
|
||||
| `LIBGL_ALWAYS_SOFTWARE=1` | Mesa uses llvmpipe, which renders on the CPU |
|
||||
| `WLR_NO_HARDWARE_CURSORS=1`, `cursor:no_hardware_cursors` | the cursor is drawn by the compositor, since there is no plane to put it on |
|
||||
|
||||
llvmpipe is slow and it draws. A machine with a real GPU never touches it.
|
||||
|
||||
Two places read the detector, because they run as different users:
|
||||
|
||||
| Where | How |
|
||||
| --- | --- |
|
||||
| the session | `default/hypr/renderer.lua`, required from `envs.lua` beside `nvidia.lua`, through `$BLOB_PATH/bin` |
|
||||
| the greeter | `default/sddm/hyprland-greeter.lua`, through `/usr/local/bin/blob-hw-gpu-accelerated` |
|
||||
|
||||
The greeter runs as SDDM's own user, with no `BLOB_PATH` and no read access to
|
||||
your home directory, so `install.sh` copies the detector to `/usr/local/bin`.
|
||||
A detector that is not there yet reads as no acceleration, which is the fallback
|
||||
that always draws.
|
||||
|
||||
To add a driver to the accelerated list, edit `accelerated_drivers` in
|
||||
`bin/blob-hw-gpu-accelerated` and rerun `./install.sh` so the greeter's copy
|
||||
follows. `virtio_gpu` is deliberately not on it: QEMU only accelerates it when
|
||||
the host enables virgl, and Mesa fails the same way when it does not.
|
||||
|
||||
To see the difference by hand, from a console:
|
||||
|
||||
```bash
|
||||
Hyprland # fails with the dri2 error
|
||||
LIBGL_ALWAYS_SOFTWARE=1 Hyprland # starts
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
`./uninstall.sh` removes the config, the user units, the `/etc` drop-ins, the
|
||||
|
||||
+47
-7
@@ -58,8 +58,11 @@ than usual.
|
||||
|
||||
Two are worth knowing about:
|
||||
|
||||
- `xdg-terminal-exec` is load-bearing. Every TUI launcher's `Exec=` line calls
|
||||
it, so if its AUR build ever fails, TUI shortcuts stop opening.
|
||||
- `xdg-terminal-exec` used to be load-bearing: every TUI launcher, the floating
|
||||
launcher behind most of the menu, and `$TERMINAL` called it by name, so a
|
||||
failed AUR build silently broke about half the menu. They all go through
|
||||
`blob-terminal-exec` now, which prefers it and falls back to `foot`, then
|
||||
`kitty`, both from the official repositories.
|
||||
- `aether` is in the AUR at a version newer than the one the repository shipped.
|
||||
|
||||
`quickshell` never came from the Omarchy repository - it is in official `extra`.
|
||||
@@ -69,13 +72,28 @@ Two are worth knowing about:
|
||||
`install.sh` installs what is missing before it writes any config:
|
||||
|
||||
```
|
||||
packages/blob.packages 119 packages from core, extra, multilib
|
||||
packages/blob-aur.packages 10 packages with no official equivalent
|
||||
packages/blob.packages 123 packages from core, extra, multilib
|
||||
packages/blob-aur.packages 11 packages with no official equivalent
|
||||
```
|
||||
|
||||
Only missing packages are touched, through `pacman -S --needed` and
|
||||
`yay -S --needed`. `./install.sh --check` lists what it would install without
|
||||
installing anything, and `--skip-packages` does the directories and config only.
|
||||
`yay -S --needed`. An entry is treated as already installed, and skipped, when a
|
||||
package of that name is installed, when an installed package provides that name,
|
||||
or when the name is a group whose members are all installed - so `base-devel` and
|
||||
packages pulled in under a different name no longer come back on every run. AUR
|
||||
packages are built one at a time, so a build that fails is reported at the end
|
||||
instead of stopping the install.
|
||||
|
||||
Repo packages go in one `pacman` call. If that call fails the databases are
|
||||
refreshed with `pacman -Sy` and it is tried again, because a stale database is
|
||||
what turns an installed-and-present package into `target not found`. If it still
|
||||
fails the packages are installed one at a time, so one unresolvable name does not
|
||||
block the other hundred - which is how a missing `sddm` used to turn into a
|
||||
machine with no login screen. `install.sh` ends by naming any of `sddm`,
|
||||
`hyprland`, `quickshell` and `uwsm` that are still not installed.
|
||||
|
||||
`./install.sh --check` lists what it would install without installing anything,
|
||||
and `--skip-packages` does the directories and config only.
|
||||
|
||||
### Validating the lists
|
||||
|
||||
@@ -111,12 +129,34 @@ Omarchy's own package list, because they were installed by hand on this machine:
|
||||
|
||||
| Package | Needed by |
|
||||
| --- | --- |
|
||||
| `python-pywal` | `blob-wallpaper-set`, palette extraction |
|
||||
| `python-pywal16` | `blob-wallpaper-set`, palette extraction |
|
||||
| `python-terminaltexteffects` | `blob-screensaver`, the effects it draws |
|
||||
| `awww` | the wallpaper daemon `blob-wallpaper-set` and `blob-theme-menu` drive |
|
||||
| `playerctl` | the quick settings media row |
|
||||
|
||||
`playerctl` is in official `extra`, so it sits in the repo list.
|
||||
|
||||
## The boot splash
|
||||
|
||||
`plymouth` is installed but left inert, and the splash is opt-in through one
|
||||
command:
|
||||
|
||||
```
|
||||
blob boot # branding/boot_flash.png
|
||||
blob boot ~/some/image.png # or any other image
|
||||
```
|
||||
|
||||
The reason it is opt-in is that an installed but *unconfigured* plymouth is not
|
||||
harmless: it takes the console at boot and never hands it back, and
|
||||
`getty@tty1` waits on `plymouth-quit-wait.service`, so the machine reaches
|
||||
neither the greeter nor a TTY. `install.sh` therefore masks
|
||||
`plymouth-start.service` and `plymouth-quit-wait.service`, and `blob boot`
|
||||
unmasks them as part of configuring everything else - the theme in
|
||||
`default/plymouth/`, the `plymouth` mkinitcpio hook, and the initramfs rebuild.
|
||||
It finishes by printing the kernel parameters your bootloader still needs
|
||||
(`quiet splash vt.global_cursor_default=0`), because that line is the one thing
|
||||
it will not rewrite for a bootloader other than GRUB.
|
||||
|
||||
## Updating
|
||||
|
||||
```
|
||||
|
||||
+53
-6
@@ -9,7 +9,7 @@ retints the running desktop.
|
||||
| File | Used by |
|
||||
| --- | --- |
|
||||
| `colors.toml` | required; the palette everything else derives from |
|
||||
| `backgrounds/` | the background switcher |
|
||||
| `backgrounds/` | the background switcher; a theme without one keeps the current wallpaper |
|
||||
| `shell.lock.toml` | lock screen surface tokens |
|
||||
| `neovim.lua` | the editor colorscheme |
|
||||
| `icons.theme` | GTK icon theme name |
|
||||
@@ -30,12 +30,14 @@ the shell and the CLI never disagree about what a slot means.
|
||||
Each rendered file is picked up differently: `foot.ini` and `kitty.conf` by an
|
||||
`include` in the shipped terminal config, `hyprland.lua` through Hyprland's Lua
|
||||
module path, `shell.toml` and `chromium.theme` read straight from the state
|
||||
directory, and `btop.theme` and `neovim.lua` through symlinks the installer
|
||||
makes into those apps' own config trees.
|
||||
directory, and `btop.theme`, `neovim.lua`, `gtk.css`, `starship.toml` and
|
||||
`fastfetch.jsonc` through symlinks the installer makes into those apps' own
|
||||
config trees.
|
||||
|
||||
`default/themed/*.tpl` are rendered per theme into
|
||||
`~/.local/state/blob/current/theme/`. Nine ship: foot, kitty, btop, chromium,
|
||||
hyprland, the screenshare picker, neovim, `shell.toml`, and zen.
|
||||
`~/.local/state/blob/current/theme/`. Twelve ship: foot, kitty, btop, chromium,
|
||||
hyprland, the screenshare picker, neovim, `shell.toml`, zen, `gtk.css`,
|
||||
`starship.toml`, and `fastfetch.jsonc`.
|
||||
|
||||
An app picks its colors up in one of two ways. Most include the generated file
|
||||
directly, so nothing has to run:
|
||||
@@ -47,11 +49,56 @@ include ~/.local/state/blob/current/theme/kitty.conf
|
||||
Zen does the same through a CSS import in `userChrome.css`. The rest are pushed
|
||||
by a short applier listed in `post_theme_commands` inside `blob-theme-set`:
|
||||
`blob-theme-foot`, `blob-theme-browser`, `blob-restart-terminal`,
|
||||
`blob-restart-btop`, `blob-hypr-restart`.
|
||||
`blob-restart-btop`, `blob-hypr-restart`, `blob-theme-gtk`.
|
||||
|
||||
## GTK apps
|
||||
|
||||
Nautilus and the other GTK apps read two things. The palette comes from
|
||||
`gtk.css`, linked into both `~/.config/gtk-3.0/gtk.css` and
|
||||
`~/.config/gtk-4.0/gtk.css`, which redefines the libadwaita named colors
|
||||
(`window_bg_color`, `headerbar_bg_color`, `accent_bg_color` and the rest) so a
|
||||
stock Adwaita app is drawn in the theme's own colors. The light or dark
|
||||
preference comes from `blob-theme-gtk`, which reads the theme's `mode` and sets
|
||||
`org.gnome.desktop.interface color-scheme` and `gtk-theme` through `gsettings`.
|
||||
|
||||
The preference applies to running apps immediately; the CSS is read at startup,
|
||||
so an app that was already open keeps its old colors until it is restarted.
|
||||
|
||||
Adding a template for another app means dropping a `.tpl` in `default/themed/`
|
||||
and, if it cannot include a file, adding an applier to that list.
|
||||
|
||||
## Tokens
|
||||
|
||||
A template may reference `{{ key }}` for any resolved palette key, plus three
|
||||
derived forms: `{{ key_strip }}` without the leading `#`, `{{ key_rgb }}` as
|
||||
`R,G,B`, and `{{ key_ansi }}` as `38;2;R;G;B`, which is what a terminal program
|
||||
that takes a raw escape sequence rather than a hex color needs.
|
||||
|
||||
## About
|
||||
|
||||
`fastfetch.jsonc` is linked to `~/.config/fastfetch/config.jsonc`. It draws
|
||||
`~/.config/blob/branding/about.txt` as the logo, with 2 columns of padding to
|
||||
its left and 6 between it and the modules - the exact numbers
|
||||
`blob-launch-about` measures the About window against, so replacing the logo
|
||||
resizes the window to fit it. Title and keys take the theme's accent.
|
||||
|
||||
A config of your own at that path is a regular file rather than the symlink, and
|
||||
`blob-launch-about` leaves the window sizing alone when it finds one.
|
||||
|
||||
## The prompt
|
||||
|
||||
`starship.toml` is linked to `~/.config/starship.toml`. It carries a
|
||||
`[palettes.blob]` block built from the theme - `accent`, `foreground`, `muted`,
|
||||
`success`, `warning`, `error` - and every style in the file names one of those
|
||||
rather than a literal color, so the prompt moves with the theme. Starship reads
|
||||
its config on each prompt, so a theme change shows up on the next command, in
|
||||
shells that are already open.
|
||||
|
||||
`starship init bash` runs from `session/bashrc`, which `install.sh` sources from
|
||||
`~/.bashrc` with a single line. That file returns immediately in a
|
||||
non-interactive shell, so it cannot disturb `scp` or `rsync`, and it also
|
||||
sources `session/env-bootstrap` so `blob` is on `PATH` in any interactive shell.
|
||||
|
||||
## The three ways to set a theme
|
||||
|
||||
| Command | Does |
|
||||
|
||||
+13
-1
@@ -63,6 +63,9 @@ link_blob_path
|
||||
install_packages
|
||||
|
||||
install_shipped_config
|
||||
refresh_font_cache
|
||||
install_shell_profile
|
||||
install_bash_integration
|
||||
install_user_units
|
||||
install_systemd_dropins
|
||||
install_zen_config
|
||||
@@ -80,7 +83,9 @@ install_login_config
|
||||
install_session_entry
|
||||
install_autologin
|
||||
enable_display_manager
|
||||
report_greeter_compositor
|
||||
report_login_conflicts
|
||||
report_desktop_essentials
|
||||
|
||||
say ""
|
||||
if [[ $check == true ]]; then
|
||||
@@ -88,7 +93,14 @@ if [[ $check == true ]]; then
|
||||
(( changes == 0 )) || exit 1
|
||||
else
|
||||
say "$changes item(s) changed."
|
||||
say "Change the theme with 'blob theme set <name>' once the session is up."
|
||||
|
||||
active_theme="$(cat "$current_state_dir/theme.name" 2>/dev/null || true)"
|
||||
if [[ -n $active_theme ]]; then
|
||||
say "Theme: $active_theme. Change it with 'blob theme set <name>'."
|
||||
fi
|
||||
|
||||
say "New shells find the blob command through /etc/profile.d/blob.sh."
|
||||
say "For this shell: source $blob_path/session/env-bootstrap"
|
||||
say ""
|
||||
launch_desktop
|
||||
fi
|
||||
|
||||
+16
-1
@@ -8,8 +8,23 @@ note_change() {
|
||||
changes=$((changes + 1))
|
||||
}
|
||||
|
||||
unit_search_dirs=(
|
||||
/etc/systemd/system
|
||||
/run/systemd/system
|
||||
/usr/local/lib/systemd/system
|
||||
/usr/lib/systemd/system
|
||||
)
|
||||
|
||||
system_unit_exists() {
|
||||
systemctl cat -- "$1" &>/dev/null
|
||||
local unit="$1" dir
|
||||
|
||||
systemctl cat -- "$unit" &>/dev/null && return 0
|
||||
|
||||
for dir in "${unit_search_dirs[@]}"; do
|
||||
[[ -e $dir/$unit ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
user_manager_available() {
|
||||
|
||||
@@ -63,6 +63,46 @@ install_shipped_config() {
|
||||
install_file "$repo_dir/fonts/omarchy.ttf" "$font_dir/omarchy.ttf" "fonts/omarchy.ttf"
|
||||
}
|
||||
|
||||
bashrc_line='[ -r "$HOME/.local/share/blob/session/bashrc" ] && . "$HOME/.local/share/blob/session/bashrc"'
|
||||
|
||||
install_bash_integration() {
|
||||
local bashrc="$HOME/.bashrc"
|
||||
|
||||
if [[ -f $bashrc ]] && grep -Fqx "$bashrc_line" "$bashrc"; then
|
||||
say "ok bashrc"
|
||||
return 0
|
||||
fi
|
||||
|
||||
note_change
|
||||
if [[ $check == true ]]; then
|
||||
say "would source session/bashrc from ~/.bashrc"
|
||||
return 0
|
||||
fi
|
||||
|
||||
[[ -f $bashrc ]] && cp "$bashrc" "$bashrc.bak"
|
||||
printf '\n%s\n' "$bashrc_line" >>"$bashrc"
|
||||
say "write bashrc"
|
||||
}
|
||||
|
||||
refresh_font_cache() {
|
||||
if ! command -v fc-cache &>/dev/null; then
|
||||
say "SKIP font cache (fc-cache not installed)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $check == true ]]; then
|
||||
say "would refresh the font cache"
|
||||
return 0
|
||||
fi
|
||||
|
||||
fc-cache -f "$font_dir" >/dev/null 2>&1 || true
|
||||
say "ok font cache"
|
||||
}
|
||||
|
||||
install_shell_profile() {
|
||||
install_root_file "$repo_dir/session/profile.sh" /etc/profile.d/blob.sh "profile.d/blob.sh"
|
||||
}
|
||||
|
||||
install_zen_config() {
|
||||
local profile
|
||||
profile="$(find "$config_dir/zen" -maxdepth 1 -type d -name '*.Default (release)*' 2>/dev/null | head -1)"
|
||||
|
||||
+42
-6
@@ -8,6 +8,7 @@ greeter_compositor_config=/etc/sddm/hyprland-greeter.lua
|
||||
# ISO, or a previous desktop left behind.
|
||||
blob_greeter_conf="$greeter_config_dir/zz-blob.conf"
|
||||
blob_autologin_conf="$greeter_config_dir/zzz-blob-autologin.conf"
|
||||
greeter_gpu_detector=/usr/local/bin/blob-hw-gpu-accelerated
|
||||
|
||||
# SDDM's stock Wayland greeter runs on weston, which Blob does not install.
|
||||
# Point it at Hyprland with a greeter-only config instead. That config is Lua:
|
||||
@@ -18,6 +19,10 @@ install_login_config() {
|
||||
"$greeter_compositor_config" "sddm/hyprland-greeter.lua"
|
||||
install_root_file "$repo_dir/default/sddm/zz-blob.conf" \
|
||||
"$blob_greeter_conf" "sddm.conf.d/zz-blob.conf"
|
||||
install_root_file "$repo_dir/bin/blob-hw-gpu-accelerated" \
|
||||
"$greeter_gpu_detector" "blob-hw-gpu-accelerated" 0755
|
||||
install_root_file "$repo_dir/default/pam.d/blob-lock-password" \
|
||||
/etc/pam.d/blob-lock-password "pam.d/blob-lock-password"
|
||||
}
|
||||
|
||||
install_session_entry() {
|
||||
@@ -54,6 +59,22 @@ setting_value() {
|
||||
sed -n "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*//p" "$file" | tail -1
|
||||
}
|
||||
|
||||
# SDDM runs CompositorCommand to get a Wayland greeter on screen. When that
|
||||
# binary is not installed SDDM fails with no greeter at all, which reads as a
|
||||
# machine that boots to a console for no reason, so name it here instead.
|
||||
report_greeter_compositor() {
|
||||
local command_line binary
|
||||
|
||||
command_line="$(setting_value "$repo_dir/default/sddm/zz-blob.conf" CompositorCommand)"
|
||||
binary="${command_line%% *}"
|
||||
|
||||
[[ -n $binary ]] || return 0
|
||||
command -v "$binary" &>/dev/null && return 0
|
||||
|
||||
say "WARN the greeter's compositor command '$binary' is not installed"
|
||||
say " SDDM cannot draw a greeter without it; it is set in $blob_greeter_conf"
|
||||
}
|
||||
|
||||
# zz-blob.conf blanks Theme and Autologin, so a leftover drop-in cannot point the
|
||||
# greeter at a theme that was uninstalled or autolog into a session that no
|
||||
# longer exists. Either one leaves SDDM on a black screen it never falls back
|
||||
@@ -85,7 +106,13 @@ report_login_conflicts() {
|
||||
# its default target is multi-user.target, which never pulls in graphical.target.
|
||||
enable_display_manager() {
|
||||
if ! system_unit_exists sddm.service; then
|
||||
say "WARN sddm.service not found; the desktop cannot start at boot"
|
||||
if package_installed sddm; then
|
||||
say "WARN sddm is installed but sddm.service is not visible to systemd"
|
||||
say " run 'sudo systemctl daemon-reload', then ./install.sh again"
|
||||
else
|
||||
say "WARN sddm is not installed; the desktop cannot start at boot"
|
||||
say " install it with 'sudo pacman -S sddm', then ./install.sh again"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -114,13 +141,19 @@ enable_display_manager() {
|
||||
fi
|
||||
|
||||
if [[ $enable_needed == true ]]; then
|
||||
as_root systemctl enable sddm.service
|
||||
say "enable sddm.service"
|
||||
if as_root systemctl enable sddm.service; then
|
||||
say "enable sddm.service"
|
||||
else
|
||||
say "WARN could not enable sddm.service"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $target_needed == true ]]; then
|
||||
as_root systemctl set-default graphical.target
|
||||
say "set default target to graphical.target"
|
||||
if as_root systemctl set-default graphical.target; then
|
||||
say "set default target to graphical.target"
|
||||
else
|
||||
say "WARN could not set the default target to graphical.target"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -137,6 +170,7 @@ launch_desktop() {
|
||||
fi
|
||||
|
||||
if ! system_unit_exists sddm.service; then
|
||||
say "No login screen to start: sddm is not installed."
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -151,5 +185,7 @@ launch_desktop() {
|
||||
fi
|
||||
|
||||
say "Starting the login screen. Pick the Blob session to log in."
|
||||
as_root systemctl start sddm.service
|
||||
as_root systemctl start sddm.service || {
|
||||
say "WARN sddm.service did not start; see 'systemctl status sddm'"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,21 @@ read_package_list() {
|
||||
grep -vE '^[[:space:]]*#|^[[:space:]]*$' "$1"
|
||||
}
|
||||
|
||||
package_installed() {
|
||||
local name="$1" member
|
||||
|
||||
pacman -Q "$name" &>/dev/null && return 0
|
||||
pacman -T "$name" &>/dev/null && return 0
|
||||
|
||||
local -a members=()
|
||||
mapfile -t members < <(pacman -Sg "$name" 2>/dev/null | awk '{print $2}')
|
||||
(( ${#members[@]} )) || return 1
|
||||
|
||||
for member in "${members[@]}"; do
|
||||
pacman -Q "$member" &>/dev/null || return 1
|
||||
done
|
||||
}
|
||||
|
||||
# A list entry may be "wanted|already-fine", which asks for the first name but
|
||||
# treats either as satisfying the requirement. That is how the AUR replacement
|
||||
# for a package still shipped by another repository avoids a file conflict.
|
||||
@@ -11,8 +26,8 @@ missing_packages() {
|
||||
for entry in $(read_package_list "$1"); do
|
||||
wanted=${entry%%|*}
|
||||
alternative=${entry#*|}
|
||||
pacman -Q "$wanted" &>/dev/null && continue
|
||||
[[ $alternative != "$entry" ]] && pacman -Q "$alternative" &>/dev/null && continue
|
||||
package_installed "$wanted" && continue
|
||||
[[ $alternative != "$entry" ]] && package_installed "$alternative" && continue
|
||||
printf '%s\n' "$wanted"
|
||||
done
|
||||
}
|
||||
@@ -43,7 +58,7 @@ install_packages() {
|
||||
|
||||
if (( ${#repo_missing[@]} )); then
|
||||
say "install ${#repo_missing[@]} repo package(s)"
|
||||
as_root pacman -S --needed --noconfirm "${repo_missing[@]}"
|
||||
install_repo_packages "${repo_missing[@]}"
|
||||
fi
|
||||
|
||||
if (( ${#aur_missing[@]} )); then
|
||||
@@ -52,10 +67,63 @@ install_packages() {
|
||||
return 0
|
||||
}
|
||||
say "install ${#aur_missing[@]} AUR package(s)"
|
||||
yay -S --needed --noconfirm "${aur_missing[@]}"
|
||||
install_aur_packages "${aur_missing[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
install_repo_packages() {
|
||||
local package
|
||||
local -a failed=()
|
||||
|
||||
as_root pacman -S --needed --noconfirm "$@" && return 0
|
||||
|
||||
say "WARN the batch install failed; refreshing the package databases"
|
||||
as_root pacman -Sy --noconfirm || true
|
||||
as_root pacman -S --needed --noconfirm "$@" && return 0
|
||||
|
||||
say "WARN still failing; retrying one package at a time"
|
||||
for package in "$@"; do
|
||||
as_root pacman -S --needed --noconfirm "$package" || failed+=("$package")
|
||||
done
|
||||
|
||||
(( ${#failed[@]} == 0 )) && return 0
|
||||
|
||||
say "WARN ${#failed[@]} repo package(s) failed to install: ${failed[*]}"
|
||||
}
|
||||
|
||||
desktop_essentials=(sddm hyprland quickshell uwsm)
|
||||
|
||||
report_desktop_essentials() {
|
||||
local package
|
||||
local -a missing=()
|
||||
|
||||
for package in "${desktop_essentials[@]}"; do
|
||||
package_installed "$package" || missing+=("$package")
|
||||
done
|
||||
|
||||
if (( ${#missing[@]} == 0 )); then
|
||||
say "ok desktop essentials"
|
||||
return 0
|
||||
fi
|
||||
|
||||
say "WARN the desktop cannot start without: ${missing[*]}"
|
||||
say " install them with 'sudo pacman -S ${missing[*]}', then ./install.sh again"
|
||||
}
|
||||
|
||||
install_aur_packages() {
|
||||
local package
|
||||
local -a failed=()
|
||||
|
||||
for package in "$@"; do
|
||||
yay -S --needed --noconfirm "$package" || failed+=("$package")
|
||||
done
|
||||
|
||||
(( ${#failed[@]} == 0 )) && return 0
|
||||
|
||||
say "WARN ${#failed[@]} AUR package(s) failed to build: ${failed[*]}"
|
||||
say "WARN the install continued; retry them with 'yay -S <name>'"
|
||||
}
|
||||
|
||||
# A fresh Arch install has no AUR helper, and yay is not in the official
|
||||
# repositories, so it is built from the AUR once with makepkg. Everything after
|
||||
# this point can then use yay normally.
|
||||
|
||||
@@ -11,8 +11,15 @@ system_services=(
|
||||
|
||||
# network-online.target must not hold up graphical.target waiting for DHCP or
|
||||
# Wi-Fi association. Nothing in the session blocks on the network.
|
||||
#
|
||||
# Blob configures no boot splash: no mkinitcpio hook, no kernel command line,
|
||||
# and no theme. An unconfigured plymouth still takes the console at boot and
|
||||
# never hands it back, and getty@tty1 waits on plymouth-quit-wait.service, so
|
||||
# the machine comes up with no greeter and no TTY to fix it from.
|
||||
masked_services=(
|
||||
NetworkManager-wait-online.service
|
||||
plymouth-start.service
|
||||
plymouth-quit-wait.service
|
||||
)
|
||||
|
||||
# blob-speaker-tuning.service is deliberately absent: blob-audio-tuning installs
|
||||
|
||||
+10
-2
@@ -37,8 +37,11 @@ link_wallpapers() {
|
||||
# like a failed session. Applied headless: no shell and no session bus exist
|
||||
# while the installer runs.
|
||||
install_default_theme() {
|
||||
if [[ -s $current_state_dir/theme.name ]]; then
|
||||
say "ok theme ($(cat "$current_state_dir/theme.name"))"
|
||||
local current_name=""
|
||||
[[ -s $current_state_dir/theme.name ]] && current_name="$(cat "$current_state_dir/theme.name")"
|
||||
|
||||
if [[ -n $current_name && -f $current_state_dir/theme/colors.toml ]]; then
|
||||
say "ok theme ($current_name)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -53,6 +56,7 @@ install_default_theme() {
|
||||
say "theme $default_theme"
|
||||
else
|
||||
say "WARN could not apply the $default_theme theme"
|
||||
say " the session will come up without a palette; run 'blob theme set $default_theme'"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -127,6 +131,10 @@ link_theme_file() {
|
||||
# creating the tree would be inventing a layout the user has not chosen.
|
||||
link_app_themes() {
|
||||
link_theme_file btop.theme "$config_dir/btop/themes/current.theme" "btop theme"
|
||||
link_theme_file gtk.css "$config_dir/gtk-3.0/gtk.css" "gtk-3.0 theme"
|
||||
link_theme_file gtk.css "$config_dir/gtk-4.0/gtk.css" "gtk-4.0 theme"
|
||||
link_theme_file starship.toml "$config_dir/starship.toml" "starship prompt"
|
||||
link_theme_file fastfetch.jsonc "$config_dir/fastfetch/config.jsonc" "fastfetch"
|
||||
|
||||
if [[ -d $config_dir/nvim/lua/plugins ]]; then
|
||||
link_theme_file neovim.lua "$config_dir/nvim/lua/plugins/theme.lua" "neovim theme"
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#
|
||||
# These have no official-repo equivalent. See docs/packages.md.
|
||||
|
||||
# Palette extraction behind blob-wallpaper-set
|
||||
python-pywal
|
||||
# Palette extraction behind blob-wallpaper-set; pywal16 is the maintained fork
|
||||
python-pywal16|python-pywal
|
||||
|
||||
# Terminal text effects behind the screensaver; ttfx was the Omarchy build
|
||||
python-terminaltexteffects
|
||||
|
||||
# Terminal dispatch; every TUI launcher's Exec= line calls it
|
||||
xdg-terminal-exec
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
# Compositor and shell
|
||||
hyprland
|
||||
mesa
|
||||
quickshell
|
||||
uwsm
|
||||
sddm
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
case $- in
|
||||
*i*) ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
|
||||
[ -r "$HOME/.local/share/blob/session/env-bootstrap" ] && . "$HOME/.local/share/blob/session/env-bootstrap"
|
||||
|
||||
command -v starship >/dev/null 2>&1 && eval "$(starship init bash)"
|
||||
|
||||
command -v zoxide >/dev/null 2>&1 && eval "$(zoxide init bash --cmd cd)"
|
||||
@@ -0,0 +1,6 @@
|
||||
# Put the blob command on PATH for login shells. The graphical session gets the
|
||||
# same environment from ~/.config/uwsm/env.d/10-blob.
|
||||
|
||||
if [ -r "$HOME/.local/share/blob/session/env-bootstrap" ]; then
|
||||
. "$HOME/.local/share/blob/session/env-bootstrap"
|
||||
fi
|
||||
@@ -1,2 +1,2 @@
|
||||
export TERMINAL=xdg-terminal-exec
|
||||
export TERMINAL=blob-terminal-exec
|
||||
export EDITOR="blob-launch-editor --inline"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
if [ -f "${BLOB_PATH%/}/session/uwsm/default" ]; then
|
||||
. "${BLOB_PATH%/}/session/uwsm/default"
|
||||
else
|
||||
export TERMINAL=xdg-terminal-exec
|
||||
export TERMINAL=blob-terminal-exec
|
||||
export EDITOR="blob-launch-editor --inline"
|
||||
fi
|
||||
|
||||
|
||||
@@ -39,8 +39,12 @@ BarWidget {
|
||||
Process {
|
||||
id: updateProc
|
||||
command: ["blob-update-available"]
|
||||
onExited: function(exitCode) {
|
||||
root.updateAvailable = exitCode === 0
|
||||
stdout: StdioCollector {
|
||||
waitForEnd: true
|
||||
onStreamFinished: {
|
||||
const pending = parseInt(String(text).trim(), 10)
|
||||
root.updateAvailable = !isNaN(pending) && pending > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ BarWidget {
|
||||
id: root
|
||||
moduleName: "blob.menu"
|
||||
|
||||
readonly property string iconPath: Quickshell.env("HOME") + "/.config/blob/branding/blob_icon.svg"
|
||||
readonly property string iconPath: Quickshell.env("HOME") + "/.config/blob/branding/chai.svg"
|
||||
readonly property int iconSize: Math.round(Style.font.body * 1.35)
|
||||
|
||||
property string iconSvg: ""
|
||||
@@ -48,7 +48,7 @@ BarWidget {
|
||||
horizontalMargin: 7.5
|
||||
onPressed: function(button) {
|
||||
if (!root.bar) return
|
||||
if (button === Qt.RightButton) root.bar.run("xdg-terminal-exec")
|
||||
if (button === Qt.RightButton) root.bar.run("blob-terminal-exec")
|
||||
else root.bar.run("blob-shell shell toggle blob.menu '{\"menu\":\"root\"}'")
|
||||
}
|
||||
|
||||
|
||||
@@ -1243,6 +1243,7 @@ Item {
|
||||
font.family: row.iconFont.length > 0 ? row.iconFont : root.fontFamily
|
||||
font.pixelSize: Style.font.iconLarge
|
||||
width: Style.space(36)
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
@@ -20,7 +20,7 @@ Item {
|
||||
Process {
|
||||
id: listProc
|
||||
command: ["bash", "-c",
|
||||
'find "$1" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" ' +
|
||||
'find -L "$1" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" ' +
|
||||
'-o -iname "*.png" -o -iname "*.webp" -o -iname "*.gif" \\) | sort',
|
||||
"--", root.directory]
|
||||
stdout: StdioCollector {
|
||||
|
||||
@@ -15,6 +15,13 @@ Item {
|
||||
|
||||
readonly property string pluginId: (manifest && manifest.id) || "blob.quick-settings"
|
||||
|
||||
readonly property int barInset: {
|
||||
var bar = root.shell ? root.shell.bar : null
|
||||
if (!bar || bar.barHidden === true) return 0
|
||||
if (String(bar.position || "top") !== "top") return 0
|
||||
return Math.max(0, bar.barSize || 0)
|
||||
}
|
||||
|
||||
function open(payloadJson) {
|
||||
root.opened = true
|
||||
}
|
||||
@@ -185,83 +192,82 @@ Item {
|
||||
onClicked: root.dismiss()
|
||||
}
|
||||
|
||||
Row {
|
||||
WeatherCard {
|
||||
anchors.top: controlCentre.top
|
||||
anchors.right: controlCentre.left
|
||||
anchors.rightMargin: Style.spaceReal(12)
|
||||
available: root.weatherAvailable
|
||||
glyph: root.weatherGlyph
|
||||
place: root.weatherPlace
|
||||
temperature: root.weatherTemperature
|
||||
wind: root.weatherWind
|
||||
}
|
||||
|
||||
Card {
|
||||
id: controlCentre
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.spaceReal(10)
|
||||
anchors.topMargin: Style.spaceReal(10) + root.barInset
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitWidth: Style.spaceReal(400)
|
||||
fillColor: Util.alpha(Color.background, Style.panelFillAlpha)
|
||||
spacing: Style.spaceReal(12)
|
||||
|
||||
WeatherCard {
|
||||
available: root.weatherAvailable
|
||||
glyph: root.weatherGlyph
|
||||
place: root.weatherPlace
|
||||
temperature: root.weatherTemperature
|
||||
wind: root.weatherWind
|
||||
Header {
|
||||
width: parent.width
|
||||
clock: clockValue.value
|
||||
today: dateValue.value
|
||||
onLockRequested: root.runAndDismiss("blob-system-lock")
|
||||
onLogoutRequested: root.runAndDismiss("blob-system-logout")
|
||||
onCloseRequested: root.dismiss()
|
||||
}
|
||||
|
||||
Card {
|
||||
id: controlCentre
|
||||
implicitWidth: Style.spaceReal(400)
|
||||
fillColor: Util.alpha(Color.background, Style.panelFillAlpha)
|
||||
spacing: Style.spaceReal(12)
|
||||
Calendar {
|
||||
width: parent.width
|
||||
polling: root.opened
|
||||
}
|
||||
|
||||
Header {
|
||||
width: parent.width
|
||||
clock: clockValue.value
|
||||
today: dateValue.value
|
||||
onLockRequested: root.runAndDismiss("blob-system-lock")
|
||||
onLogoutRequested: root.runAndDismiss("blob-system-logout")
|
||||
onCloseRequested: root.dismiss()
|
||||
Tiles {
|
||||
bluetoothOn: bluetoothFlag.value
|
||||
doNotDisturb: dndFlag.value
|
||||
nightLight: nightLightFlag.value
|
||||
recording: recordingFlag.value
|
||||
stayAwake: stayAwakeFlag.value
|
||||
tabletFollow: tabletFlag.value
|
||||
onRunRequested: function(command) { root.runAndDismiss(command) }
|
||||
onPanelRequested: function(pluginId) { root.openPanel(pluginId) }
|
||||
onToggleRequested: function(command) { root.toggleAndRefresh(command) }
|
||||
}
|
||||
|
||||
SliderRow {
|
||||
width: parent.width
|
||||
icon: mutedFlag.value ? "" : ""
|
||||
dimmed: mutedFlag.value
|
||||
value: Number(volumeValue.value) || 0
|
||||
onIconActivated: root.toggleAndRefresh("pamixer --toggle-mute")
|
||||
onValueRequested: function(next) {
|
||||
root.run("pamixer --set-volume " + next)
|
||||
volumeValue.value = String(next)
|
||||
}
|
||||
}
|
||||
|
||||
Calendar {
|
||||
width: parent.width
|
||||
polling: root.opened
|
||||
SliderRow {
|
||||
width: parent.width
|
||||
icon: ""
|
||||
value: Number(brightnessValue.value) || 0
|
||||
onValueRequested: function(next) {
|
||||
root.run("brightnessctl set " + next + "%")
|
||||
brightnessValue.value = String(next)
|
||||
}
|
||||
}
|
||||
|
||||
Tiles {
|
||||
bluetoothOn: bluetoothFlag.value
|
||||
doNotDisturb: dndFlag.value
|
||||
nightLight: nightLightFlag.value
|
||||
recording: recordingFlag.value
|
||||
stayAwake: stayAwakeFlag.value
|
||||
tabletFollow: tabletFlag.value
|
||||
onRunRequested: function(command) { root.runAndDismiss(command) }
|
||||
onPanelRequested: function(pluginId) { root.openPanel(pluginId) }
|
||||
onToggleRequested: function(command) { root.toggleAndRefresh(command) }
|
||||
}
|
||||
|
||||
SliderRow {
|
||||
width: parent.width
|
||||
icon: mutedFlag.value ? "" : ""
|
||||
dimmed: mutedFlag.value
|
||||
value: Number(volumeValue.value) || 0
|
||||
onIconActivated: root.toggleAndRefresh("pamixer --toggle-mute")
|
||||
onValueRequested: function(next) {
|
||||
root.run("pamixer --set-volume " + next)
|
||||
volumeValue.value = String(next)
|
||||
}
|
||||
}
|
||||
|
||||
SliderRow {
|
||||
width: parent.width
|
||||
icon: ""
|
||||
value: Number(brightnessValue.value) || 0
|
||||
onValueRequested: function(next) {
|
||||
root.run("brightnessctl set " + next + "%")
|
||||
brightnessValue.value = String(next)
|
||||
}
|
||||
}
|
||||
|
||||
MediaRow {
|
||||
width: parent.width
|
||||
title: root.mediaParts.length > 0 ? root.mediaParts[0] : ""
|
||||
artist: root.mediaParts.length > 1 ? root.mediaParts[1] : ""
|
||||
status: root.mediaParts.length > 2 ? root.mediaParts[2] : "Stopped"
|
||||
onControlRequested: function(action) {
|
||||
root.run("playerctl " + action)
|
||||
stateSettleTimer.restart()
|
||||
}
|
||||
MediaRow {
|
||||
width: parent.width
|
||||
title: root.mediaParts.length > 0 ? root.mediaParts[0] : ""
|
||||
artist: root.mediaParts.length > 1 ? root.mediaParts[1] : ""
|
||||
status: root.mediaParts.length > 2 ? root.mediaParts[2] : "Stopped"
|
||||
onControlRequested: function(action) {
|
||||
root.run("playerctl " + action)
|
||||
stateSettleTimer.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-22
@@ -1,23 +1,31 @@
|
||||
accent = "#7babe3"
|
||||
cursor = "#cbd2d9"
|
||||
foreground = "#cbd2d9"
|
||||
background = "#0f1012"
|
||||
selection_foreground = "#0f1012"
|
||||
selection_background = "#7babe3"
|
||||
mode = "dark"
|
||||
|
||||
color0 = "#000000"
|
||||
color1 = "#d53838"
|
||||
color2 = "#38d4d5"
|
||||
color3 = "#d58638"
|
||||
color4 = "#d53886"
|
||||
color5 = "#38d586"
|
||||
color6 = "#3886d5"
|
||||
color7 = "#d4d4d4"
|
||||
color8 = "#949494"
|
||||
color9 = "#d53838"
|
||||
color10 = "#38d4d5"
|
||||
color11 = "#d58638"
|
||||
color12 = "#d53886"
|
||||
color13 = "#38d586"
|
||||
color14 = "#3886d5"
|
||||
color15 = "#d4d4d4"
|
||||
accent = "#5fa3e8"
|
||||
selection = "#1e2228"
|
||||
muted = "#5c656e"
|
||||
|
||||
background = "#0f1012"
|
||||
dark_background = "#0a0b0c"
|
||||
darker_background = "#050607"
|
||||
lighter_background = "#191c20"
|
||||
|
||||
foreground = "#cbd2d9"
|
||||
dark_foreground = "#78828c"
|
||||
light_foreground = "#dde3e9"
|
||||
bright_foreground = "#f0f4f8"
|
||||
|
||||
red = "#d53838"
|
||||
yellow = "#d58638"
|
||||
orange = "#d56338"
|
||||
green = "#38d586"
|
||||
cyan = "#38d4d5"
|
||||
blue = "#3886d5"
|
||||
magenta = "#d53886"
|
||||
brown = "#6a321c"
|
||||
|
||||
bright_red = "#dd6060"
|
||||
bright_yellow = "#dd9e60"
|
||||
bright_green = "#60dd9e"
|
||||
bright_cyan = "#60dddd"
|
||||
bright_blue = "#609edd"
|
||||
bright_magenta = "#dd609e"
|
||||
|
||||
@@ -7,6 +7,9 @@ config_dir="$HOME/.config"
|
||||
session_entry="/usr/local/share/wayland-sessions/blob.desktop"
|
||||
|
||||
root_files=(
|
||||
/etc/profile.d/blob.sh
|
||||
/usr/local/bin/blob-hw-gpu-accelerated
|
||||
/etc/pam.d/blob-lock-password
|
||||
/etc/sddm.conf.d/zz-blob.conf
|
||||
/etc/sddm.conf.d/zzz-blob-autologin.conf
|
||||
/etc/sddm/hyprland-greeter.conf
|
||||
@@ -110,6 +113,13 @@ for lua in hyprland.lua monitors.lua input.lua looknfeel.lua bindings.lua autost
|
||||
restore_or_remove "$config_dir/hypr/$lua" "hypr/$lua"
|
||||
done
|
||||
|
||||
[[ -L $config_dir/starship.toml ]] && restore_or_remove "$config_dir/starship.toml" "starship.toml"
|
||||
[[ -L $config_dir/fastfetch/config.jsonc ]] && restore_or_remove "$config_dir/fastfetch/config.jsonc" "fastfetch/config.jsonc"
|
||||
|
||||
for gtk in gtk-3.0 gtk-4.0; do
|
||||
[[ -L $config_dir/$gtk/gtk.css ]] && restore_or_remove "$config_dir/$gtk/gtk.css" "$gtk/gtk.css"
|
||||
done
|
||||
|
||||
if [[ $keep_state == false && -d $HOME/.local/state/blob ]]; then
|
||||
rm -rf "$HOME/.local/state/blob"
|
||||
say "remove ~/.local/state/blob"
|
||||
|
||||
Reference in New Issue
Block a user