refactor(hyprlua): move hypr-usr/ into hypr/usr/ and wire sysupdate sync

Device-specific Lua configs (monitors, binds, input, etc.) previously lived
in a top-level hypr-usr/ sibling to hypr/. They now live inside hypr/usr/
so all hypr-related configs sit under one directory tree.

- git mv hypr-usr/ → hypr/usr/
- hyprland.lua: require() calls updated to require("usr.*")
- input.lua: require("input-device-exceptions") → require("usr.input-device-exceptions")
- hyprpaper.conf + monitorhandler.sh + wallpaper-picker: wallpaper.conf path
  updated from ~/.config/wallpaper.conf → ~/.config/hypr/usr/wallpaper.conf
- monitorhandler.sh: fix stale desktopenvs/hyprland/ → desktopenvs/hyprlua/
- binds.lua: edit-binds keybind path corrected to ~/.config/hypr/usr/binds.lua
- helpmenu.sh: update binds.lua source path
- updater.conf: SOURCE_BASE fixed (hyprland→hyprlua), config hypr except usr,
  remove flat hypr-usr entry
- update-configs.sh: add "except <subdirs>" support for the config type
- hyprlua.sh installer: remove now-redundant hypr-usr copy lines
- migrate-hyprland-to-hyprlua.sh: deploy from hypr/usr/ instead of hypr-usr/
- sysupdate.sh: add CONFIG SYNC section with _migrate_hypr_usr() which moves
  old flat-layout files into usr/ (case 1) or copies fresh from dotfiles when
  no local config exists (case 2); fallback copy preserves hypr/usr/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main^2
Amir Alexander Abdelbaki 2026-06-11 15:46:01 +02:00
parent 9638fd337a
commit 7fd0e37061
21 changed files with 253 additions and 43 deletions

View File

@ -3,6 +3,14 @@
#
# Config: ~/.config/config-updater/updater.conf
# Manifest: ~/.config/config-updater/manifest
#
# Syntax in updater.conf:
# config <name> [except <sub1> <sub2> ...]
# copy SOURCE_BASE/<name> to ~/.config/<name>, optionally skipping subdirs
# flat <name>
# copy contents of SOURCE_BASE/<name> directly into ~/.config/
# ignore <name>
# present in source but intentionally not managed here
set -euo pipefail
@ -23,6 +31,7 @@ die() { err "$*"; exit 1; }
# ── parse updater.conf ────────────────────────────────────────────────────────
SOURCE_BASE=""
declare -A ENTRY_TYPE # name → config | flat | ignore
declare -A ENTRY_EXCL # name → space-separated list of excluded subdirs
while IFS= read -r line; do
line="${line%%#*}"
@ -36,10 +45,19 @@ while IFS= read -r line; do
continue
fi
read -r type name _rest <<< "$line"
read -r type name rest <<< "$line"
[[ -z "${name:-}" ]] && continue
case "$type" in
config|flat|ignore) ENTRY_TYPE["$name"]="$type" ;;
config|flat|ignore)
ENTRY_TYPE["$name"]="$type"
# Parse optional "except sub1 sub2 ..."
if [[ "${rest:-}" =~ ^except[[:space:]]+(.+)$ ]]; then
ENTRY_EXCL["$name"]="${BASH_REMATCH[1]}"
else
ENTRY_EXCL["$name"]=""
fi
;;
*) warn "Unknown type '$type' for '$name' — skipping" ;;
esac
done < "$CONF_FILE"
@ -91,11 +109,35 @@ for name in "${!ENTRY_TYPE[@]}"; do
continue
fi
excl="${ENTRY_EXCL[$name]:-}"
case "$type" in
config)
if [[ -z "$excl" ]]; then
rm -rf "${TARGET:?}/${name}"
cp -r "$src" "$TARGET/$name"
ok "config $name"
else
# Copy all top-level items of $src except excluded subdirs
mkdir -p "${TARGET}/${name}"
item_errors=0
while IFS= read -r -d '' item; do
item_name="$(basename "$item")"
skip_item=false
for e in $excl; do
[[ "$item_name" == "$e" ]] && skip_item=true && break
done
[[ "$skip_item" == true ]] && continue
rm -rf "${TARGET:?}/${name}/${item_name}"
cp -r "$item" "${TARGET}/${name}/" || (( item_errors++ )) || true
done < <(find "$src" -maxdepth 1 -mindepth 1 -print0 | sort -z)
if (( item_errors > 0 )); then
err "config $name (${item_errors} error(s))"
(( errors++ )) || true
else
ok "config $name (excl: $excl)"
fi
fi
;;
flat)
[[ -d "$src" ]] || {

View File

@ -7,14 +7,14 @@
# (for installation-specific files that live at the ~/.config root)
# ignore <name> present in source but intentionally not managed here
SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprland
SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprlua
# ── deployed as ~/.config/<name> ─────────────────────────────────────────────
config alacritty
config btop
config dunst
config gtk-3.0
config hypr
config hypr except usr
config kitty
config mimeapps.list
config nwg-dock-hyprland
@ -27,9 +27,6 @@ config walker
config wofi
config xfce4
# ── flat: directory contents copied directly into ~/.config/ ──────────────────
flat hypr-usr # installation-specific: binds, monitors, autostart, etc.
# ── intentionally not managed here ───────────────────────────────────────────
ignore config-updater # the updater itself
ignore CRT # referenced from dotfiles path directly in binds.conf
@ -40,3 +37,7 @@ ignore greetd-tuigreet # deployed to /etc/greetd/ at install time
ignore spicetify # managed separately (spicetify handles its own config)
ignore Vencord # managed separately
ignore waybar # present but inactive; eww bar is used instead
# hypr/usr/ contains device-specific lua files (monitors, binds, input, etc.).
# They are excluded from automated syncs to preserve per-device customisations.
# Deploy manually on a new device:
# cp -r ~/Dotfiles/desktopenvs/hyprlua/hypr/usr/ ~/.config/hypr/

View File

@ -1,12 +1,12 @@
-- Hyprland Lua config — https://wiki.hypr.land/Configuring/Start/
-- User-side files are required from ~/.config/hypr/ alongside this file.
-- Device-specific files live in ~/.config/hypr/usr/ (deployed from hypr/usr/).
require("monitors")
require("envvars")
require("input")
require("binds")
require("windowrules")
require("autostart")
require("usr.monitors")
require("usr.envvars")
require("usr.input")
require("usr.binds")
require("usr.windowrules")
require("usr.autostart")
--------------------
---- MY PROGRAMS ---

View File

@ -8,4 +8,4 @@ wallpaper {
}
# per-monitor state, written by ~/.config/scripts/wallpaper-picker
source = ~/.config/wallpaper.conf
source = ~/.config/hypr/usr/wallpaper.conf

View File

@ -61,7 +61,7 @@ hl.bind(mainMod .. " + W", hl.dsp.exec_cmd("[tag +centered-L] kitt
hl.bind(mainMod .. " + CTRL + R", hl.dsp.exec_cmd("[tag +centered-L] kitty -e ~/.config/scripts/amssh"))
hl.bind(mainMod .. " + F1", hl.dsp.exec_cmd("[tag +centered] kitty ~/.config/scripts/helpmenu.sh"))
hl.bind(mainMod .. " + CTRL + T", hl.dsp.exec_cmd("[tag +centered-S] kitty bash ~/.config/scripts/timer-pick"))
hl.bind(mainMod .. " + SHIFT + F1", hl.dsp.exec_cmd("[tag +centered-L] kitty nvim ~/.config/binds.lua"))
hl.bind(mainMod .. " + SHIFT + F1", hl.dsp.exec_cmd("[tag +centered-L] kitty nvim ~/.config/hypr/usr/binds.lua"))
hl.bind(mainMod .. " + CTRL + P", hl.dsp.exec_cmd("~/.config/scripts/screenrec.sh"))

View File

@ -15,4 +15,4 @@ hl.config({
},
})
require("input-device-exceptions")
require("usr.input-device-exceptions")

View File

@ -1,3 +1,3 @@
#!/bin/bash
cat ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/binds.lua | less
cat ~/Dotfiles/desktopenvs/hyprlua/hypr/usr/binds.lua | less

View File

@ -1,4 +1,4 @@
#!/bin/bash
hyprpaper -c ~/.config/wallpaper.conf
~/Dotfiles/desktopenvs/hyprland/scripts/ewwstart.sh
hyprpaper -c ~/.config/hypr/usr/wallpaper.conf
~/Dotfiles/desktopenvs/hyprlua/scripts/ewwstart.sh

View File

@ -6,7 +6,7 @@ set -u
VERBOSE=0
DIR=""
STATE_FILE="${HOME}/.config/wallpaper.conf"
STATE_FILE="${HOME}/.config/hypr/usr/wallpaper.conf"
HYPRLOCK_BG_FILE="${HOME}/.config/hypr/hyprlock-backgrounds.conf"
FIT_MODE="cover"

View File

@ -99,8 +99,8 @@ done
# ── swap hypr-usr (the part that actually differs) ────────────────────────────
# hyprland: *.conf files live at ~/.config/ root (sourced by hyprland.conf)
# hyprlua: *.lua files live at ~/.config/hypr/ (required by hyprland.lua)
# wallpaper.conf stays at ~/.config/ root (hyprpaper still reads it)
# hyprlua: device-specific files live at ~/.config/hypr/usr/
# (required via require("usr.*") in hyprland.lua)
printf '\n'
@ -108,20 +108,17 @@ printf '\n'
for f in "${HYPR_USR_CONFS[@]}"; do
[[ -f "$TARGET/$f" ]] && run "rm '$TARGET/$f'" && note "removed ~/.config/$f"
done
# Remove wallpaper.conf from ~/.config/ root if present (old location)
[[ -f "$TARGET/wallpaper.conf" ]] && run "rm '$TARGET/wallpaper.conf'" && note "removed ~/.config/wallpaper.conf"
# Deploy new .lua files into ~/.config/hypr/
if [[ -d "$SRC/hypr-usr" ]]; then
run "mkdir -p '$TARGET/hypr'"
for lua in "$SRC/hypr-usr"/*.lua; do
[[ -e "$lua" ]] || continue
run "cp '$lua' '$TARGET/hypr/'"
ok "hypr-usr $(basename "$lua") → ~/.config/hypr/"
# Deploy device-specific files into ~/.config/hypr/usr/
if [[ -d "$SRC/hypr/usr" ]]; then
run "mkdir -p '$TARGET/hypr/usr'"
for f in "$SRC/hypr/usr"/*; do
[[ -e "$f" ]] || continue
run "cp '$f' '$TARGET/hypr/usr/'"
ok "hypr/usr $(basename "$f") → ~/.config/hypr/usr/"
done
# wallpaper.conf stays at ~/.config/ root
if [[ -f "$SRC/hypr-usr/wallpaper.conf" ]]; then
run "cp '$SRC/hypr-usr/wallpaper.conf' '$TARGET/'"
ok "hypr-usr wallpaper.conf → ~/.config/"
fi
fi
# ── update config-updater ─────────────────────────────────────────────────────

View File

@ -103,11 +103,8 @@ for cfg in "${CONFIGS[@]}"; do
rm -rf ~/.config/"$cfg"
cp -r ~/Dotfiles/desktopenvs/hyprlua/"$cfg" ~/.config/
done
# User-side Lua files live inside ~/.config/hypr/ so require() finds them
cp ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/*.lua ~/.config/hypr/
# wallpaper.conf stays at ~/.config/ for hyprpaper
cp ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/wallpaper.conf ~/.config/
# hypr/usr/ (device-specific) is already inside hypr/ and copied above.
# Customise ~/.config/hypr/usr/ per device after install.
cp ~/Dotfiles/colors.conf ~/.config/colors.conf

View File

@ -14,6 +14,7 @@ set -uo pipefail
readonly STATE_FILE="/updatestate"
readonly SCRIPT_PREFIX="/updatescript"
readonly NEWS_FEED="https://archlinux.org/feeds/news/"
DOTFILES="${DOTFILES:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
AI_MODE=false
for _arg in "$@"; do [[ "$_arg" == "--AI" ]] && AI_MODE=true; done
@ -485,6 +486,169 @@ PROMPT_EOF
fi
}
# ═══════════════════════════════════════════════════════════════════════════════
# CONFIG SYNC
# ═══════════════════════════════════════════════════════════════════════════════
# Items always skipped in the direct-copy fallback (managed by separate
# installers, or intentionally not auto-synced). hypr/usr/ (device-specific)
# is handled separately inside the copy loop rather than via this list.
declare -a _CFG_SKIP=(
config-updater # meta: the updater script itself
CRT eww eww-nobattery eww-touch greetd-tuigreet spicetify Vencord waybar
)
# Resolve the dotfiles DE source directory for the current machine.
# Priority: installed updater.conf SOURCE_BASE → running session detection.
_de_source_dir() {
local conf="${XDG_CONFIG_HOME:-$HOME/.config}/config-updater/updater.conf"
if [[ -f "$conf" ]]; then
local src
src=$(awk -F'[[:space:]]*=[[:space:]]*' '/^SOURCE_BASE/{print $2; exit}' "$conf")
src="${src/#\~/$HOME}"
[[ -d "${src:-}" ]] && printf '%s' "$src" && return
fi
if pgrep -x Hyprland &>/dev/null || [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
printf '%s' "$DOTFILES/desktopenvs/hyprlua"
elif pgrep -x niri &>/dev/null || [[ -n "${NIRI_SOCKET:-}" ]]; then
printf '%s' "$DOTFILES/desktopenvs/niri"
fi
}
# Detect and offer to migrate the old hyprlua flat layout:
# old: ~/.config/hypr/monitors.lua etc. (pre-usr/ era)
# new: ~/.config/hypr/usr/monitors.lua
_migrate_hypr_usr() {
local target="${XDG_CONFIG_HOME:-$HOME/.config}"
local hypr_dir="$target/hypr"
local usr_dir="$hypr_dir/usr"
# Already in the right place — nothing to do
[[ -d "$usr_dir" ]] && return 0
# ── Case 1: old flat layout — lua files at ~/.config/hypr/ root ──────────
if [[ -d "$hypr_dir" ]]; then
local -a old_files=()
for f in monitors.lua envvars.lua input.lua binds.lua windowrules.lua \
autostart.lua input-device-exceptions.lua; do
[[ -f "$hypr_dir/$f" ]] && old_files+=("$f")
done
if [[ ${#old_files[@]} -gt 0 ]]; then
echo
warn "Old hyprlua layout: device-specific files at ${BO}~/.config/hypr/${RS} root."
warn "New location: ${BO}~/.config/hypr/usr/${RS}"
echo
if ! ask "Migrate now?"; then
warn "Skipped — hyprland.lua will fail to start until migrated."
return 1
fi
mkdir -p "$usr_dir"
for f in "${old_files[@]}"; do
mv "$hypr_dir/$f" "$usr_dir/"
ok "moved hypr/$f → hypr/usr/$f"
done
if [[ -f "$target/wallpaper.conf" && ! -f "$usr_dir/wallpaper.conf" ]]; then
mv "$target/wallpaper.conf" "$usr_dir/wallpaper.conf"
ok "moved wallpaper.conf → hypr/usr/wallpaper.conf"
fi
echo
ok "Migration complete — device configs now at ${BO}~/.config/hypr/usr/${RS}"
return 0
fi
fi
# ── Case 2: no usr/ and no old files — copy template from dotfiles ───────
local de_dir; de_dir=$(_de_source_dir)
local src_usr="${de_dir:+$de_dir/hypr/usr}"
if [[ -n "${src_usr:-}" && -d "$src_usr" ]]; then
mkdir -p "$usr_dir"
cp -r "$src_usr/." "$usr_dir/"
ok "Copied ${BO}hypr/usr/${RS} from dotfiles ${DI}(no local config found)${RS}"
fi
}
sync_configs() {
section "Config Sync"
# ── Migration: old flat layout → hypr/usr/ ───────────────────────────────
_migrate_hypr_usr
# ── Preferred: use the installed update-configs.sh ───────────────────────
local cfg_script
for cfg_script in "$HOME/update-configs.sh" \
"${XDG_CONFIG_HOME:-$HOME/.config}/config-updater/update-configs.sh"; do
[[ -x "$cfg_script" ]] && break || cfg_script=""
done
if [[ -n "${cfg_script:-}" ]]; then
log "Using installed config-updater (${DI}${cfg_script}${RS})"
echo
if "$cfg_script"; then
echo
ok "Config sync complete"
else
echo
warn "Config sync completed with errors"
fi
return
fi
# ── Fallback: direct copy from dotfiles ──────────────────────────────────
local de_dir; de_dir=$(_de_source_dir)
if [[ -z "${de_dir:-}" || ! -d "$de_dir" ]]; then
warn "Cannot determine DE source directory — config sync skipped"
warn "Hint: deploy config-updater or ensure a supported DE session is running"
return
fi
local target="${XDG_CONFIG_HOME:-$HOME/.config}"
log "Source → ${BO}${de_dir}${RS}"
log "Target → ${BO}${target}${RS}"
warn "${BO}hypr/usr/${RS} preserved (device-specific — customise manually)"
echo
local synced=0 errs=0
while IFS= read -r -d '' item; do
local name; name="$(basename "$item")"
local skip=false
for s in "${_CFG_SKIP[@]}"; do [[ "$name" == "$s" ]] && skip=true && break; done
[[ "$skip" == true ]] && continue
if [[ "$name" == "hypr" ]]; then
# Copy hypr/ contents but preserve ~/.config/hypr/usr/
mkdir -p "$target/hypr"
local hypr_ok=true
while IFS= read -r -d '' hitem; do
local hname; hname="$(basename "$hitem")"
[[ "$hname" == "usr" ]] && continue
rm -rf "${target}/hypr/${hname}" && cp -r "$hitem" "$target/hypr/" \
|| hypr_ok=false
done < <(find "$item" -maxdepth 1 -mindepth 1 -print0 | sort -z)
if $hypr_ok; then
ok "synced hypr ${DI}(usr/ preserved)${RS}"
(( synced++ )) || true
else
err "failed hypr"
(( errs++ )) || true
fi
elif rm -rf "${target:?}/${name}" && cp -r "$item" "$target/"; then
ok "synced ${name}"
(( synced++ )) || true
else
err "failed ${name}"
(( errs++ )) || true
fi
done < <(find "$de_dir" -maxdepth 1 -mindepth 1 -not -name '.*' -print0 | sort -z)
echo
if (( errs > 0 )); then
warn "Config sync: ${synced} synced, ${errs} error(s)"
else
ok "Config sync: ${BO}${synced}${RS} config(s) synced"
fi
}
# ═══════════════════════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════════════════════
@ -526,6 +690,10 @@ main() {
echo
ok "${BO}System is up to date.${RS}"
write_state
echo
if ask "Sync dotfiles configs to ~/.config?"; then
sync_configs
fi
rm -f "$_NEWS_PY"
exit 0
fi
@ -678,6 +846,11 @@ main() {
fi
echo
# ── Config sync ──────────────────────────────────────────────────────────
if ask "Sync dotfiles configs to ~/.config?"; then
sync_configs
fi
rm -f "$_NEWS_PY"
}