nixos/shared/modules/services/theme_switcher/README.md
2026-01-23 13:44:07 -07:00

448 lines
13 KiB
Markdown

# 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