# Dynamic Theme Switcher Module A NixOS module that automatically generates and applies color themes from wallpapers using pywal16, with integrated wpaperd wallpaper management and support for GTK, Qt/Kvantum, Ghostty, and other applications. ## Features - **Integrated Wallpaper Management**: Built-in wpaperd integration with configurable rotation modes - **Automatic Color Extraction**: Uses pywal16 with configurable backends (haishoku, colorthief, etc.) to extract color palettes from wallpapers - **Multi-Application Support**: Generates themes for GTK 3.0, GTK 4.0, Qt/Kvantum, Ghostty terminal, and more - **Time-Based Switching**: Automatically switches between light and dark themes based on configured times - **Flexible Rotation Modes**: Choose between continuous rotation, switch-only, or hybrid modes - **Manual Control**: Provides scripts for manual theme switching - **Synchronized Wallpaper & Theme**: Wallpaper and color theme always match ## Directory Structure ``` theme_switcher/ ├── default.nix # Main module configuration ├── templates/ │ ├── gtk-3.0.css # GTK 3.0 theme template │ ├── gtk-4.0.css # GTK 4.0 theme template │ └── PywalTheme.kvconfig # Kvantum Qt theme template ├── OUTLINE.md # Implementation specification └── README.md # This file ``` ## Installation ### 1. Import the Module Add the module to your host configuration: ```nix # In your host's configuration.nix or desktop-configuration.nix imports = [ ../../shared/modules/services/theme_switcher ]; ``` ### 2. Enable and Configure ```nix services.themeSwitcher = { enable = true; user = "yourUsername"; # Optional: Customize settings backend = "haishoku"; # Color extraction backend lightTime = "06:00:00"; # Switch to light theme at 6 AM darkTime = "18:00:00"; # Switch to dark theme at 6 PM enableAutoSwitch = true; # Enable automatic time-based switching # Wallpaper rotation settings rotation = { mode = "continuous"; # Options: continuous, switch-only, hybrid interval = "5m"; # Rotation interval (for continuous mode) }; # Wpaperd configuration wpaperd = { enable = true; mode = "center"; # Options: center, fit, stretch, tile transition.effect = "fade"; transition.duration = 300; # milliseconds }; }; ``` **Note:** If you were previously using the separate `wallpaper-rotator` module, you should disable it to avoid conflicts. ### 3. Wallpaper Directory Structure Ensure your wallpaper directory has Light and Dark subdirectories: ``` wallpapers/ ├── Light/ │ ├── wallpaper1.jpg │ ├── wallpaper2.jpg │ └── ... └── Dark/ ├── wallpaper1.jpg ├── wallpaper2.jpg └── ... ``` ### 4. Rebuild Your System ```bash sudo nixos-rebuild switch --flake .#hostname ``` ## Configuration Options ### Core Options #### `services.themeSwitcher.enable` - **Type**: boolean - **Default**: false - **Description**: Enable the dynamic theme switcher with integrated wallpaper management #### `services.themeSwitcher.user` - **Type**: string - **Required**: yes - **Description**: Username for which to apply theme switching #### `services.themeSwitcher.wallpaperPath` - **Type**: string - **Default**: `${homeDirectory}/nixos/shared/modules/services/wallpapers` - **Description**: Path to wallpaper directories (must contain Light/ and Dark/ subdirectories) #### `services.themeSwitcher.backend` - **Type**: enum - **Default**: `"haishoku"` - **Options**: `"haishoku"`, `"modern_colorthief"`, `"fast_colorthief"`, `"colorthief"`, `"colorz"`, `"wal"` - **Description**: Pywal color extraction backend algorithm #### `services.themeSwitcher.lightTime` - **Type**: string - **Default**: `"06:00:00"` - **Description**: Time to switch to light theme (HH:MM:SS format) #### `services.themeSwitcher.darkTime` - **Type**: string - **Default**: `"18:00:00"` - **Description**: Time to switch to dark theme (HH:MM:SS format) #### `services.themeSwitcher.enableAutoSwitch` - **Type**: boolean - **Default**: true - **Description**: Enable automatic time-based theme switching via systemd timer ### Rotation Options #### `services.themeSwitcher.rotation.mode` - **Type**: enum - **Default**: `"continuous"` - **Options**: - `"continuous"`: Rotate wallpapers every interval AND regenerate theme each time - `"switch-only"`: Only change wallpaper at light/dark switch times (6 AM/6 PM) - `"hybrid"`: Rotate wallpapers but only regenerate theme at switch times (not yet implemented) - **Description**: Wallpaper rotation and theme generation behavior #### `services.themeSwitcher.rotation.interval` - **Type**: string - **Default**: `"5m"` - **Example**: `"30s"`, `"10m"`, `"1h"` - **Description**: How often to rotate wallpapers in continuous mode #### `services.themeSwitcher.rotation.sorting` - **Type**: enum - **Default**: `"random"` - **Options**: `"random"` - **Description**: Wallpaper selection method (currently only random is supported) ### Wpaperd Integration Options #### `services.themeSwitcher.wpaperd.enable` - **Type**: boolean - **Default**: true - **Description**: Enable wpaperd wallpaper daemon integration #### `services.themeSwitcher.wpaperd.mode` - **Type**: enum - **Default**: `"center"` - **Options**: `"center"`, `"fit"`, `"fit-border-color"`, `"stretch"`, `"tile"` - **Description**: How to display wallpapers when size differs from display resolution #### `services.themeSwitcher.wpaperd.transition.effect` - **Type**: string - **Default**: `"fade"` - **Example**: `"simple"`, `"fade"` - **Description**: Wallpaper transition effect name #### `services.themeSwitcher.wpaperd.transition.duration` - **Type**: integer - **Default**: 300 - **Description**: Transition duration in milliseconds ## Manual Usage ### Apply Theme Manually The module installs `apply-theme.sh` to `~/.local/bin/`: ```bash # Apply light theme with random wallpaper from Light/ directory ~/.local/bin/apply-theme.sh --light # Apply dark theme with random wallpaper from Dark/ directory ~/.local/bin/apply-theme.sh --dark # Apply theme with specific wallpaper ~/.local/bin/apply-theme.sh --light --wallpaper-path /path/to/wallpaper.jpg ``` ### Check Systemd Timer Status ```bash # View timer status systemctl --user status theme-switcher.timer # List upcoming timer events systemctl --user list-timers | grep theme # Manually trigger theme switch systemctl --user start theme-switcher.service # View service logs journalctl --user -u theme-switcher.service ``` ### Test Pywal Color Extraction ```bash # Test light theme generation wal -i ~/nixos/shared/modules/services/wallpapers/Light/IU-Light.jpg --backend haishoku -l -n # Test dark theme generation wal -i ~/nixos/shared/modules/services/wallpapers/Dark/IU-Dark.jpg --backend haishoku -n # View generated colors cat ~/.cache/wal/colors.json # View generated templates ls -la ~/.cache/wal/ ``` ## Generated Files After running the theme switcher, pywal generates the following files: ### Pywal Cache (`~/.cache/wal/`) - `colors.json` - Extracted color palette in JSON format - `colors.sh` - Shell script with color variables - `ghostty.conf` - Ghostty terminal theme (auto-reloads) - `gtk-3.0.css` - GTK 3.0 theme (from custom template) - `gtk-4.0.css` - GTK 4.0 theme (from custom template) - `PywalTheme.kvconfig` - Kvantum Qt theme (from custom template) ### User Configuration - `~/.config/gtk-3.0/gtk.css` - Symlink to pywal-generated GTK3 theme - `~/.config/gtk-4.0/gtk.css` - Symlink to pywal-generated GTK4 theme - `~/.config/Kvantum/PywalTheme/PywalTheme.kvconfig` - Kvantum theme copy ## Integration with Existing Themes When enabling this module, you should adjust your existing theme configuration: ### Before (Static Catppuccin Theme) ```nix gtk = { enable = true; theme = { name = "catppuccin-macchiato-lavender-compact+rimless"; package = pkgs.catppuccin-gtk.override { ... }; }; }; qt = { enable = true; platformTheme.name = "kvantum"; style.name = "kvantum"; }; xdg.configFile."Kvantum/kvantum.kvconfig".source = ...; ``` ### After (Dynamic Pywal Theme) ```nix # The theme switcher module automatically sets: # - gtk.enable = true # - qt.enable = true # - qt.platformTheme.name = "kvantum" # - qt.style.name = "kvantum" # You can still keep static icon and cursor themes: gtk.iconTheme = { name = "Papirus-Dark"; package = pkgs.catppuccin-papirus-folders; }; gtk.cursorTheme = { name = "Bibata-Modern-Classic"; package = pkgs.bibata-cursors; }; ``` ## Troubleshooting ### Theme Not Applying ```bash # Check if pywal generated files ls -la ~/.cache/wal/ # Check systemd service status systemctl --user status theme-switcher.service # View detailed logs journalctl --user -u theme-switcher.service -n 50 # Manually run apply-theme.sh with verbose output ~/.local/bin/apply-theme.sh --light ``` ### Kvantum Theme Not Working ```bash # Check if Kvantum theme was created ls -la ~/.config/Kvantum/PywalTheme/ # Manually set Kvantum theme kvantummanager --set PywalTheme # Check if Qt applications are using Kvantum echo $QT_STYLE_OVERRIDE # Should be "kvantum" ``` ### Colors Don't Match Wallpaper Try a different pywal backend: ```nix services.themeSwitcher.backend = "modern_colorthief"; # or "fast_colorthief", "colorthief", etc. ``` ### GTK Applications Not Updating ```bash # Check if GTK CSS files exist ls -la ~/.config/gtk-3.0/gtk.css ls -la ~/.config/gtk-4.0/gtk.css # Restart GTK applications for changes to take effect ``` ## Supported Applications ### Automatically Themed - **GTK 3 Applications**: Nautilus, GNOME Calculator, etc. - **GTK 4 Applications**: GNOME Text Editor, newer GNOME apps - **Qt Applications**: KDE apps when using Kvantum - **Ghostty Terminal**: Auto-reloads on config change - **Waybar**: If using pywal template (built-in) - **Rofi**: If using pywal template (built-in) - **Hyprland/Sway**: If using pywal template (built-in) ### Manual Integration Required - **Firefox**: Use pywal-firefox extension - **VSCode**: Use pywal theme extension - **Other Applications**: Check if pywal templates exist in `~/.cache/wal/` ## Advanced Usage ### Custom Pywal Templates To add your own templates, create files in `~/.config/wal/templates/`: ```bash # Template file: ~/.config/wal/templates/myapp.conf # Use pywal template variables background = {background} foreground = {foreground} color0 = {color0} ... color15 = {color15} ``` After running `apply-theme.sh`, find your generated file in `~/.cache/wal/myapp.conf`. ### Disable Automatic Switching To use manual control only: ```nix services.themeSwitcher = { enable = true; user = "username"; enableAutoSwitch = false; # Disable timer }; ``` Then use `~/.local/bin/apply-theme.sh` manually as needed. ### Rotation Modes Explained #### Continuous Mode (Default) - Wallpaper rotates every interval (e.g., 5 minutes) - Theme is regenerated from each new wallpaper - Wallpaper and theme always match - **Resource usage**: Moderate (pywal runs every interval) - **Best for**: Users who want frequently changing, perfectly matched themes #### Switch-Only Mode - Wallpaper changes only at light/dark switch times (6 AM / 6 PM) - Theme is generated once per switch - Wallpaper and theme always match - **Resource usage**: Minimal (pywal runs twice per day) - **Best for**: Users who prefer consistent themes throughout the day #### Hybrid Mode (Future) - Wallpaper rotates every interval - Theme is generated only at switch times - Wallpaper and theme may not match mid-day - **Not yet implemented** ### Migration from wallpaper-rotator Module If you were previously using the separate `wallpaper-rotator` module: 1. **Disable the old module** in your configuration: ```nix # Comment out or remove: # services.wallpaperRotator.enable = true; ``` 2. **Enable themeSwitcher** with equivalent settings: ```nix services.themeSwitcher = { enable = true; user = "yourUsername"; rotation.interval = "5m"; # Same as old duration setting wpaperd.mode = "center"; # Same as old mode setting }; ``` 3. **Rebuild** your system - wpaperd will now be managed by themeSwitcher ## Technical Details - **Pywal Template Syntax**: `{background}`, `{foreground}`, `{color0}`-`{color15}`, `{cursor}`, `{wallpaper}` - **Template Location**: `~/.config/wal/templates/filename.ext` - **Output Location**: `~/.cache/wal/filename.ext` - **Color Extraction**: 16-color palette using pywal16 - **GTK Reload**: Automatic via gsettings changes - **Kvantum Reload**: May require app restart - **Ghostty Reload**: Automatic on config file change ## Future Enhancements - Direct wpaperd integration (trigger theme change on wallpaper rotation) - Per-monitor theme configuration - Additional application templates (Firefox, VSCode, etc.) - Theme preview before applying - Fallback color schemes for monochrome wallpapers ## Credits - Built for NixOS using home-manager - Uses [pywal16](https://github.com/eylles/pywal16) for color extraction - Based on [pywal](https://github.com/dylanaraps/pywal) original concept