Add conflict handling for home-manager symlinks

- Handle conflicting symlinks for icons, konsole, and fish directories
- Backup existing directories before home-manager creates new ones
- Run conflict handling before linkGeneration phase
This commit is contained in:
Celes Renata
2025-08-10 11:47:18 -07:00
parent 5f15f5ed78
commit efae617d52
9 changed files with 419 additions and 41 deletions
+1 -1
View File
@@ -101,7 +101,7 @@ bind = $Primary$Secondary, Slash, exec, pkill anyrun || anyrun
# ##################################### Quickshell keybinds #####################################
# Quickshell reload and reset
bindr = $Primary$Secondary, R, exec, hyprctl reload; pkill quickshell; pkill activewin.sh; pkill activews.sh; pkill gohypr; pkill bash; pkill ydotool; ~/.local/bin/quickshell-reset.sh; /home/celes/sources/celesrenata/end-4-dev/quickshell-wrapper.sh &
bindr = $Primary$Secondary, R, exec, hyprctl reload; pkill quickshell; pkill activewin.sh; pkill activews.sh; pkill gohypr; pkill bash; pkill ydotool; ~/.local/bin/quickshell-reset.sh; qs &
# Color generation and theming
bind = $Primary$Secondary, T, exec, /home/celes/sources/celesrenata/end-4-dev/generate-colors.sh
+8 -10
View File
@@ -24,15 +24,13 @@
{
overlays.default = final: prev: {
# Enhanced quickshell with Qt5Compat support for dots-hyprland
quickshell = final.symlinkJoin {
name = "quickshell-with-qt5compat";
paths = [ quickshell.packages.${system}.default ];
buildInputs = [ final.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/qs \
--prefix QML2_IMPORT_PATH : "${final.qt6.qt5compat}/lib/qt-6/qml"
'';
};
quickshell = final.writeShellScriptBin "qs" ''
# Add Qt5Compat and quickshell config directories to QML import path
export QML2_IMPORT_PATH="${final.kdePackages.qt5compat}/lib/qt-6/qml:$HOME/.config/quickshell/ii:$HOME/.config/quickshell:$QML2_IMPORT_PATH"
# Execute the original quickshell
exec ${quickshell.packages.${system}.default}/bin/qs "$@"
'';
};
packages.${system} = utilityPackages // {
@@ -72,7 +70,7 @@
enable = true;
source = ./configs; # Use local configs
packageSet = "essential";
mode = "declarative";
mode = "hybrid";
# 🎨 Quickshell Configuration
quickshell = {
+3 -16
View File
@@ -7,21 +7,8 @@ let
cfg = config.programs.dots-hyprland.quickshell;
mainCfg = config.programs.dots-hyprland;
# Our working quickshell build with QtPositioning support
workingQuickshell =
let
quickshellSrc = pkgs.fetchFromGitHub {
owner = "quickshell-mirror";
repo = "quickshell";
rev = "a5431dd02dc23d9ef1680e67777fed00fe5f7cda";
hash = "sha256-vqkSDvh7hWhPvNjMjEDV4KbSCv2jyl2Arh73ZXe274k=";
};
quickshellBase = pkgs.callPackage (quickshellSrc + "/default.nix") {
debug = true;
gitRev = "a5431dd02dc23d9ef1680e67777fed00fe5f7cda";
};
in
quickshellBase.withModules (with pkgs.qt6; [ qtpositioning qtmultimedia ]);
# Use the wrapped quickshell from our overlay (includes Qt5Compat support)
workingQuickshell = pkgs.quickshell;
# Service startup script that handles initial setup
quickshellStartup = pkgs.writeShellScript "quickshell-startup" ''
@@ -133,7 +120,7 @@ EOF
log "🚀 App launcher: $LAUNCHER_WRAPPER"
# Start quickshell
exec ${workingQuickshell}/bin/quickshell -p "$CONFIG_DIR/quickshell/ii/shell.qml"
exec ${workingQuickshell}/bin/qs -p "$CONFIG_DIR/quickshell/ii/shell.qml"
'';
in
+2 -3
View File
@@ -44,9 +44,8 @@ in
# MISC configs (everything except fish and hypr)
(mkIf cfg.copyMiscConfig (
let
# Get all directories in .config except fish and hypr
# Get all directories in .config except fish, hypr, and quickshell (quickshell handled specially)
configDirs = [
"quickshell"
"kitty"
"foot"
"fuzzel"
@@ -58,7 +57,7 @@ in
configFiles = listToAttrs (map (dir: {
name = dir;
value = {
source = "${cfg.source}/.config/${dir}";
source = "${cfg.source}/${dir}";
recursive = true;
};
}) configDirs);
+93 -1
View File
@@ -6,6 +6,7 @@ with lib;
let
cfg = config.programs.dots-hyprland;
packages = import ../packages { inherit pkgs; };
in
{
imports = [
@@ -127,10 +128,101 @@ in
# Enable custom keybindings
# Set critical environment variable (required for both modes)
# Set critical environment variables (required for both modes)
home.sessionVariables = {
ILLOGICAL_IMPULSE_VIRTUAL_ENV = "$HOME/.local/state/quickshell/.venv";
};
# Ensure ~/.local/bin is in PATH for our working qs script
home.sessionPath = [ "$HOME/.local/bin" ];
# Generate qmldir files for all modes (runs after all config is in place)
home.activation.generateQmldirFiles = lib.hm.dag.entryAfter ["linkGeneration"] ''
if [[ -d "$HOME/.config/quickshell/ii" ]]; then
$DRY_RUN_CMD echo "🔧 Generating qmldir files with singleton detection..."
$DRY_RUN_CMD ${packages.generate-qmldir}/bin/generate-qmldir "$HOME/.config/quickshell/ii"
$DRY_RUN_CMD echo " qmldir files generated successfully for ${cfg.mode} mode"
else
$DRY_RUN_CMD echo " Warning: quickshell/ii directory not found, skipping qmldir generation"
fi
'';
# Create working qs script with Qt5Compat support
home.activation.createWorkingQsScript = lib.hm.dag.entryAfter ["linkGeneration"] ''
$DRY_RUN_CMD echo "🔧 Creating working qs script with Qt5Compat support..."
$DRY_RUN_CMD mkdir -p "$HOME/.local/bin"
$DRY_RUN_CMD cat > "$HOME/.local/bin/qs" << 'EOF'
#!/usr/bin/env bash
export QML2_IMPORT_PATH="${pkgs.kdePackages.qt5compat}/lib/qt-6/qml:$HOME/.config/quickshell/ii:$HOME/.config/quickshell:$QML2_IMPORT_PATH"
exec quickshell "$@"
EOF
$DRY_RUN_CMD chmod +x "$HOME/.local/bin/qs"
$DRY_RUN_CMD echo " Working qs script created successfully"
# Also install the quickshell reset script
$DRY_RUN_CMD echo "🔧 Installing quickshell reset script..."
$DRY_RUN_CMD cp "${packages.quickshell-reset}/bin/quickshell-reset.sh" "$HOME/.local/bin/"
$DRY_RUN_CMD chmod +x "$HOME/.local/bin/quickshell-reset.sh"
$DRY_RUN_CMD echo " Quickshell reset script installed successfully"
'';
# Custom activation script to copy quickshell configs (needed for relative imports)
home.activation.copyQuickshellConfigs = lib.hm.dag.entryBefore ["linkGeneration"] ''
$DRY_RUN_CMD echo "🔧 Setting up quickshell configuration for ${cfg.mode} mode..."
# Remove any existing symlinked configs to avoid conflicts with system home-manager
if [[ -L "$HOME/.config/quickshell" ]]; then
$DRY_RUN_CMD rm "$HOME/.config/quickshell"
$DRY_RUN_CMD echo " Removed conflicting symlinked quickshell config"
fi
# Handle conflicting .local/share symlinks that may interfere with home-manager
if [[ -L "$HOME/.local/share/icons" ]]; then
$DRY_RUN_CMD rm "$HOME/.local/share/icons"
$DRY_RUN_CMD echo " Removed conflicting symlinked icons directory"
fi
if [[ -L "$HOME/.local/share/konsole" ]]; then
$DRY_RUN_CMD rm "$HOME/.local/share/konsole"
$DRY_RUN_CMD echo " Removed conflicting symlinked konsole directory"
fi
# Handle conflicting .config directories
if [[ -L "$HOME/.config/fish" ]]; then
$DRY_RUN_CMD rm "$HOME/.config/fish"
$DRY_RUN_CMD echo " Removed conflicting symlinked fish config"
fi
# Also handle if they exist as regular directories
if [[ -d "$HOME/.local/share/konsole" && ! -L "$HOME/.local/share/konsole" ]]; then
$DRY_RUN_CMD mv "$HOME/.local/share/konsole" "$HOME/.local/share/konsole.backup-$(date +%Y%m%d-%H%M%S)"
$DRY_RUN_CMD echo " Backed up existing konsole directory"
fi
if [[ -d "$HOME/.config/fish" && ! -L "$HOME/.config/fish" ]]; then
$DRY_RUN_CMD mv "$HOME/.config/fish" "$HOME/.config/fish.backup-$(date +%Y%m%d-%H%M%S)"
$DRY_RUN_CMD echo " Backed up existing fish config directory"
fi
'';
# Copy quickshell config after link generation
home.activation.setupQuickshellConfig = lib.hm.dag.entryAfter ["linkGeneration"] ''
${optionalString (cfg.mode == "hybrid") ''
# Copy quickshell config to enable relative imports
if [[ ! -d "$HOME/.config/quickshell" ]] || [[ -L "$HOME/.config/quickshell" ]]; then
$DRY_RUN_CMD mkdir -p "$HOME/.config"
$DRY_RUN_CMD cp -r "${cfg.source}/quickshell" "$HOME/.config/"
$DRY_RUN_CMD chmod -R u+w "$HOME/.config/quickshell"
$DRY_RUN_CMD echo " Quickshell configuration copied successfully"
else
$DRY_RUN_CMD echo " Quickshell configuration already exists"
fi
# Ensure our working qs script takes precedence over any system-managed quickshell
$DRY_RUN_CMD mkdir -p "$HOME/.local/bin"
$DRY_RUN_CMD echo " Ensuring ~/.local/bin is in PATH for hybrid mode"
''}
'';
# Ensure XDG directories exist (installer requirement)
xdg.enable = true;
+12 -7
View File
@@ -1,19 +1,24 @@
# Package definitions for dots-hyprland utilities
{ pkgs }:
let
scriptsPath = ./scripts;
in
{
update-flake = pkgs.writeShellScriptBin "update-flake"
(builtins.readFile "${scriptsPath}/update-flake.sh");
(builtins.readFile ./scripts/update-flake.sh);
test-python-env = pkgs.writeShellScriptBin "test-python-env"
(builtins.readFile "${scriptsPath}/test-python-env.sh");
(builtins.readFile ./scripts/test-python-env.sh);
test-quickshell = pkgs.writeShellScriptBin "test-quickshell"
(builtins.readFile "${scriptsPath}/test-quickshell.sh");
(builtins.readFile ./scripts/test-quickshell.sh);
compare-modes = pkgs.writeShellScriptBin "compare-modes"
(builtins.readFile "${scriptsPath}/compare-modes.sh");
(builtins.readFile ./scripts/compare-modes.sh);
# QML directory generator for quickshell
generate-qmldir = pkgs.writeShellScriptBin "generate-qmldir"
(builtins.readFile ./scripts/generate-qmldir.sh);
# Quickshell reset script
quickshell-reset = pkgs.writeShellScriptBin "quickshell-reset.sh"
(builtins.readFile ./scripts/quickshell-reset.sh);
}
+9 -3
View File
@@ -3,6 +3,9 @@
{ lib, pkgs }:
let
# Import utility packages
utilityPackages = import ./default.nix { inherit pkgs; };
# illogical-impulse-basic PKGBUILD
basicPackages = with pkgs; [
axel
@@ -36,6 +39,7 @@ let
kdePackages.qt5compat # For Qt5Compat.GraphicalEffects
kdePackages.qtdeclarative # For QML
kdePackages.qtwayland # For Wayland support
kdePackages.qtpositioning # For Weather service location features
];
# illogical-impulse-hyprland PKGBUILD
@@ -106,11 +110,13 @@ in
themePackages;
# Combined package sets for different use cases
essentialPackages = basicPackages ++ widgetPackages ++ hyprlandPackages;
essentialPackages = basicPackages ++ widgetPackages ++ hyprlandPackages ++
(builtins.attrValues utilityPackages);
allPackages = basicPackages ++ widgetPackages ++ hyprlandPackages ++
pythonSystemPackages ++ audioPackages ++ fontPackages ++ themePackages;
pythonSystemPackages ++ audioPackages ++ fontPackages ++ themePackages ++
(builtins.attrValues utilityPackages);
# Minimal set for testing
minimalPackages = basicPackages ++ widgetPackages;
minimalPackages = basicPackages ++ widgetPackages ++ (builtins.attrValues utilityPackages);
}
+223
View File
@@ -0,0 +1,223 @@
#!/usr/bin/env bash
# Dynamic qmldir generator for quickshell configurations
# This script automatically generates qmldir files based on actual QML files present
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log() {
echo -e "${GREEN}[qmldir-gen]${NC} $1" >&2
}
warn() {
echo -e "${YELLOW}[qmldir-gen]${NC} WARNING: $1" >&2
}
error() {
echo -e "${RED}[qmldir-gen]${NC} ERROR: $1" >&2
}
# Function to check if a QML file is a singleton
is_singleton() {
local file="$1"
grep -q "pragma Singleton" "$file" 2>/dev/null
}
# Function to generate qmldir for a directory
generate_qmldir() {
local dir="$1"
local module_name="$2"
local qmldir_file="$dir/qmldir"
if [[ ! -d "$dir" ]]; then
warn "Directory $dir does not exist, skipping"
return 0
fi
log "Generating qmldir for $dir (module: $module_name)"
# Start with module declaration
echo "module $module_name" > "$qmldir_file"
echo "" >> "$qmldir_file"
# Find all QML files and add them
local count=0
local singleton_count=0
while IFS= read -r -d '' file; do
local basename=$(basename "$file" .qml)
# Skip files that start with lowercase (usually internal components)
if [[ "$basename" =~ ^[A-Z] ]]; then
if is_singleton "$file"; then
echo "singleton $basename 1.0 $(basename "$file")" >> "$qmldir_file"
((singleton_count++))
else
echo "$basename 1.0 $(basename "$file")" >> "$qmldir_file"
fi
((count++))
fi
done < <(find "$dir" -maxdepth 1 -name "*.qml" -type f -print0 | sort -z) || true
log " → Registered $count components ($singleton_count singletons) in $qmldir_file"
}
# Main function
main() {
local quickshell_dir="${1:-$HOME/.config/quickshell/ii}"
if [[ ! -d "$quickshell_dir" ]]; then
error "Quickshell directory $quickshell_dir does not exist"
exit 1
fi
log "Generating qmldir files for quickshell configuration at $quickshell_dir"
# Generate main qmldir (root level components)
log "Processing root directory..."
generate_qmldir "$quickshell_dir" "qs"
# Copy main qmldir to qs subdirectory for proper module resolution
if [[ -f "$quickshell_dir/qmldir" && -d "$quickshell_dir/qs" ]]; then
log "Copying main qmldir to qs subdirectory for module resolution..."
cp "$quickshell_dir/qmldir" "$quickshell_dir/qs/qmldir"
log " → Main qmldir copied to qs/qmldir"
# Also copy the root-level QML files that are referenced in the qmldir
log "Copying root-level QML files to qs subdirectory..."
while IFS= read -r qml_file; do
if [[ -f "$quickshell_dir/$qml_file" ]]; then
cp "$quickshell_dir/$qml_file" "$quickshell_dir/qs/$qml_file"
log " → Copied $qml_file to qs/$qml_file"
fi
done < <(grep "\.qml$" "$quickshell_dir/qmldir" | awk '{print $NF}' || true)
fi
# Generate qmldir for modules directory
if [[ -d "$quickshell_dir/modules" ]]; then
log "Processing modules directory..."
generate_qmldir "$quickshell_dir/modules" "qs.modules"
# Generate qmldir for each module subdirectory
while IFS= read -r -d '' module_dir; do
local module_name=$(basename "$module_dir")
log "Processing module: $module_name"
generate_qmldir "$module_dir" "qs.modules.$module_name"
# Handle nested subdirectories (like common/functions, common/widgets)
while IFS= read -r -d '' nested_dir; do
local nested_name=$(basename "$nested_dir")
log "Processing nested module: $module_name/$nested_name"
generate_qmldir "$nested_dir" "qs.modules.$module_name.$nested_name"
done < <(find "$module_dir" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) || true
done < <(find "$quickshell_dir/modules" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z)
fi
# Generate qmldir for services directory
if [[ -d "$quickshell_dir/services" ]]; then
log "Processing services directory..."
generate_qmldir "$quickshell_dir/services" "qs.services"
# Generate qmldir for each service subdirectory
while IFS= read -r -d '' service_dir; do
local service_name=$(basename "$service_dir")
log "Processing service: $service_name"
generate_qmldir "$service_dir" "qs.services.$service_name"
done < <(find "$quickshell_dir/services" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z)
fi
# Generate qmldir for qs directory (if it exists)
if [[ -d "$quickshell_dir/qs" ]]; then
log "Processing qs directory..."
# Don't overwrite the main qmldir file we copied earlier
# Only generate qmldir for subdirectories of qs
# Generate qmldir for qs subdirectories
while IFS= read -r -d '' qs_subdir; do
local subdir_name=$(basename "$qs_subdir")
log "Processing qs/$subdir_name"
generate_qmldir "$qs_subdir" "qs.qs.$subdir_name"
# Handle nested qs subdirectories (like qs/modules/common, qs/services/ai)
while IFS= read -r -d '' nested_qs_dir; do
local nested_name=$(basename "$nested_qs_dir")
log "Processing nested qs: $subdir_name/$nested_name"
generate_qmldir "$nested_qs_dir" "qs.qs.$subdir_name.$nested_name"
# Handle deeply nested directories (like qs/modules/common/functions)
while IFS= read -r -d '' deep_nested_dir; do
local deep_name=$(basename "$deep_nested_dir")
log "Processing deep nested qs: $subdir_name/$nested_name/$deep_name"
generate_qmldir "$deep_nested_dir" "qs.qs.$subdir_name.$nested_name.$deep_name"
done < <(find "$nested_qs_dir" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) || true
done < <(find "$qs_subdir" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) || true
done < <(find "$quickshell_dir/qs" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z)
fi
log "✅ qmldir generation complete!"
log "📊 Summary:"
find "$quickshell_dir" -name "qmldir" -type f | while read -r qmldir_file; do
local component_count=$(grep -c "\.qml$" "$qmldir_file" || echo "0")
local singleton_count=$(grep -c "^singleton" "$qmldir_file" || echo "0")
local relative_path=${qmldir_file#$quickshell_dir/}
log "$relative_path: $component_count components ($singleton_count singletons)"
done
}
# Help function
show_help() {
cat << EOF
Dynamic qmldir Generator for Quickshell
USAGE:
$(basename "$0") [QUICKSHELL_DIR]
ARGUMENTS:
QUICKSHELL_DIR Path to quickshell configuration directory
Default: \$HOME/.config/quickshell/ii
DESCRIPTION:
This script automatically generates qmldir files for quickshell configurations
by scanning for QML files and registering them appropriately. This ensures
that all components are properly registered and available for import.
The script handles:
- Root level components (ReloadPopup, etc.)
- Module components (bar, overview, etc.)
- Service components
- Nested subdirectories (functions, widgets, etc.)
- Automatic singleton detection via "pragma Singleton"
- Complex nested directory structures (qs/modules/common/functions)
Singleton Detection:
The script automatically detects QML files with "pragma Singleton" declarations
and registers them with the "singleton" keyword in qmldir files. This is
essential for proper Material You theming and service registration.
EXAMPLES:
# Generate for default location
$(basename "$0")
# Generate for custom location
$(basename "$0") /path/to/quickshell/config
# Use in automation
$(basename "$0") && quickshell -c ii
EOF
}
# Parse arguments
case "${1:-}" in
-h|--help)
show_help
exit 0
;;
*)
main "$@"
;;
esac
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# Quickshell reset script for end-4-flakes
# This script resets quickshell and regenerates qmldir files
set -e
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
log() {
echo -e "${GREEN}[quickshell-reset]${NC} $1"
}
warn() {
echo -e "${YELLOW}[quickshell-reset]${NC} WARNING: $1"
}
error() {
echo -e "${RED}[quickshell-reset]${NC} ERROR: $1"
}
log "Starting quickshell reset..."
# Kill any running quickshell processes
log "Stopping quickshell processes..."
pkill -f quickshell || true
pkill -f qs || true
# Wait a moment for processes to stop
sleep 1
# Regenerate qmldir files if the generator exists
if command -v generate-qmldir >/dev/null 2>&1; then
if [[ -d "$HOME/.config/quickshell/ii" ]]; then
log "Regenerating qmldir files..."
generate-qmldir "$HOME/.config/quickshell/ii"
log "qmldir files regenerated successfully"
else
warn "Quickshell config directory not found, skipping qmldir generation"
fi
else
warn "qmldir generator not found in PATH, skipping regeneration"
fi
# Clear any cached QML modules
log "Clearing QML cache..."
rm -rf "$HOME/.cache/quickshell" 2>/dev/null || true
rm -rf "$HOME/.cache/qml" 2>/dev/null || true
# Ensure our working qs script exists
if [[ ! -f "$HOME/.local/bin/qs" ]]; then
warn "Working qs script not found, creating it..."
mkdir -p "$HOME/.local/bin"
cat > "$HOME/.local/bin/qs" << 'EOF'
#!/usr/bin/env bash
export QML2_IMPORT_PATH="/nix/store/777j0jxpj9v2j3ykk70d02lph2qmr1xg-qt5compat-6.9.1/lib/qt-6/qml:$HOME/.config/quickshell/ii:$HOME/.config/quickshell:$QML2_IMPORT_PATH"
exec quickshell "$@"
EOF
chmod +x "$HOME/.local/bin/qs"
log "Working qs script created"
fi
log "Quickshell reset complete!"
log "You can now start quickshell with: qs"