400 lines
13 KiB
Markdown
400 lines
13 KiB
Markdown
# NixOS Dynamic Theme Switcher - Implementation Instructions
|
|
|
|
## Objective
|
|
Build a time-based dynamic theme switching system on NixOS that:
|
|
1. Extracts color palettes from wallpapers using pywal16 with haishoku backend
|
|
2. Generates and applies themes for GTK, Qt/Kvantum, Ghostty, and other applications
|
|
3. Switches between light/dark themes based on time of day
|
|
4. Integrates with existing wpaperd wallpaper rotation service
|
|
|
|
## Current System State
|
|
|
|
### Existing Theming Configuration
|
|
**GTK Configuration:**
|
|
- Current theme: `catppuccin-macchiato-lavender-compact+rimless`
|
|
- Settings location: `~/.config/gtk-3.0/settings.ini`, `~/.config/gtk-4.0/settings.ini`
|
|
- Icon theme: `Papirus-Dark`
|
|
- Cursor: `Bibata-Modern-Classic`
|
|
- Files are symlinked from Nix store via home-manager
|
|
|
|
**Qt/Kvantum Configuration:**
|
|
- Current theme: `catppuccin-macchiato-lavender`
|
|
- Location: `~/.config/Kvantum/kvantum.kvconfig`
|
|
- Style: kvantum (both Qt5 and Qt6)
|
|
- Environment variable: `QT_STYLE_OVERRIDE=kvantum`
|
|
|
|
**Wallpaper Service:**
|
|
- Using wpaperd with home-manager integration
|
|
- Wallpapers in: `/home/nate/nixos/shared/modules/services/wallpapers/`
|
|
- Organized into `Light/` and `Dark/` subdirectories
|
|
- Service configured via `services.wallpaperRotator` module
|
|
|
|
**Actions Required:**
|
|
1. Disable/modify static GTK theme configuration in home.nix
|
|
2. Disable/modify static Kvantum theme configuration
|
|
3. Integrate with wpaperd to trigger theme changes on wallpaper rotation
|
|
4. Remove hardcoded Catppuccin theme packages when pywal themes are working
|
|
|
|
## Implementation Components
|
|
|
|
### 3. Install Required Packages
|
|
|
|
Add to module configuration:
|
|
```nix
|
|
home.packages = with pkgs; [
|
|
pywal16 # Color scheme generator with 16-color support
|
|
python3Packages.haishoku # Recommended backend for pywal16
|
|
wpaperd # Already installed via wallpaper service
|
|
];
|
|
```
|
|
|
|
**Available pywal16 backends:** modern_colorthief, wal, fast_colorthief, haishoku, colorthief, colorz, okthief, schemer2
|
|
|
|
### 4. Color Extraction & Theme Generation
|
|
|
|
#### 4.1 Pywal16 Built-in Templates
|
|
**Pywal16 already includes templates for:**
|
|
- Ghostty: `ghostty.conf` template (tested and confirmed)
|
|
- CSS variables: `colors.css` template
|
|
- Waybar: `colors-waybar.css`
|
|
- Hyprland: `colors-hyprland.conf`
|
|
- Rofi: `colors-rofi-dark.rasi` and `colors-rofi-light.rasi`
|
|
- Sway: `colors-sway`
|
|
- Mako: `colors-mako`
|
|
- Foot, Kitty, Alacritty terminal configs
|
|
- Many others (47 templates total)
|
|
|
|
**Output location:** `~/.cache/wal/` after running `wal -i /path/to/wallpaper`
|
|
|
|
**Usage:**
|
|
```bash
|
|
wal -i /path/to/wallpaper --backend haishoku -n
|
|
# -n flag skips setting wallpaper (wpaperd handles that)
|
|
# --backend haishoku uses the haishoku color extraction
|
|
# -l flag for light themes
|
|
```
|
|
|
|
#### 4.2 GTK Theme Generation
|
|
**Goal:** Create custom GTK 3.0 and GTK 4.0 CSS themes from pywal colors
|
|
|
|
**Note:** Pywal16 includes `colors.css` template with CSS variables, but NOT full GTK themes.
|
|
|
|
**Required files:**
|
|
- `~/.config/gtk-3.0/gtk.css` - Custom CSS overlay
|
|
- `~/.config/gtk-4.0/gtk.css` - Custom CSS overlay
|
|
|
|
**Approach:** Create pywal template to generate GTK CSS using colors.json output
|
|
- Map pywal colors to GTK color definitions
|
|
- Use @define-color for GTK theme variables
|
|
- Apply to common widgets
|
|
|
|
#### 4.3 Qt/Kvantum Theme Generation
|
|
**Goal:** Generate Kvantum theme from pywal colors
|
|
|
|
**Kvantum structure (.kvconfig file):**
|
|
- `[General]` section with metadata
|
|
- `[GeneralColors]` section with color definitions (window.color, base.color, button.color, etc.)
|
|
- `[Hacks]` section with tweaks
|
|
- Per-widget sections (PanelButtonCommand, etc.)
|
|
|
|
**Location:** `~/.config/Kvantum/PywalTheme/PywalTheme.kvconfig`
|
|
|
|
**Required:** Create pywal template that maps colors.json to .kvconfig format
|
|
**Optional:** SVG file can be minimal or omitted for basic themes
|
|
|
|
**Application:** Use `kvantummanager --set PywalTheme` to activate
|
|
|
|
### 5. Theme Application Script
|
|
|
|
**Script: `apply-theme.sh`**
|
|
|
|
**Parameters:**
|
|
- `--light` or `--dark` (theme mode)
|
|
- Optional: `--wallpaper-path` (specific wallpaper, otherwise use wpaperd current)
|
|
|
|
**Script Logic:**
|
|
|
|
1. **Determine wallpaper source:**
|
|
- If `--wallpaper-path` provided, use it
|
|
- Otherwise, let wpaperd select based on mode (Light/ or Dark/ directory)
|
|
- Use `wpaperctl` to interact with wpaperd if needed
|
|
|
|
2. **Generate color scheme:**
|
|
```bash
|
|
if [ "$MODE" = "light" ]; then
|
|
wal -i "$WALLPAPER" --backend haishoku -l -n
|
|
else
|
|
wal -i "$WALLPAPER" --backend haishoku -n
|
|
fi
|
|
```
|
|
|
|
3. **Apply GTK theme:**
|
|
```bash
|
|
# pywal doesn't generate full GTK themes, use custom CSS
|
|
ln -sf ~/.cache/wal/gtk-3.0.css ~/.config/gtk-3.0/gtk.css
|
|
ln -sf ~/.cache/wal/gtk-4.0.css ~/.config/gtk-4.0/gtk.css
|
|
|
|
gsettings set org.gnome.desktop.interface color-scheme "prefer-$MODE"
|
|
```
|
|
|
|
4. **Apply Qt/Kvantum theme:**
|
|
```bash
|
|
kvantummanager --set PywalTheme
|
|
```
|
|
|
|
5. **Reload configurations:**
|
|
- Ghostty: Auto-reloads config on file change (no action needed)
|
|
- GTK apps: Automatically detect gsettings changes
|
|
- Qt apps: May need restart for Kvantum changes
|
|
- Hyprland/Sway: Reload via IPC if using pywal templates
|
|
|
|
6. **Optional: Update Flatpak:**
|
|
```bash
|
|
flatpak override --user --filesystem=~/.cache/wal:ro
|
|
```
|
|
|
|
### 6. Wallpaper Integration & Time-Based Switching
|
|
|
|
**Option A: Integrate with wpaperd events (Recommended)**
|
|
- Modify wallpaper-rotator module to support Light/Dark subdirectories
|
|
- Change wallpaper directory based on time of day
|
|
- Trigger theme generation on wallpaper change
|
|
|
|
**Option B: Independent systemd timer**
|
|
|
|
**Timer configuration:**
|
|
```nix
|
|
systemd.user.timers.theme-switcher = {
|
|
Unit.Description = "Dynamic Theme Switcher Timer";
|
|
Timer = {
|
|
OnCalendar = [ "*-*-* 06:00:00" "*-*-* 18:00:00" ]; # 6am light, 6pm dark
|
|
Persistent = true;
|
|
};
|
|
Install.WantedBy = [ "timers.target" ];
|
|
};
|
|
```
|
|
|
|
**Service configuration:**
|
|
```nix
|
|
systemd.user.services.theme-switcher = {
|
|
Unit.Description = "Dynamic Theme Switcher";
|
|
Service = {
|
|
Type = "oneshot";
|
|
ExecStart = "${pkgs.bash}/bin/bash ${./scripts/theme-switcher.sh}";
|
|
Environment = "PATH=${lib.makeBinPath [ pkgs.pywal16 pkgs.coreutils ]}";
|
|
};
|
|
};
|
|
```
|
|
|
|
**Script determines light/dark based on current hour:**
|
|
```bash
|
|
hour=$(date +%H)
|
|
if [ $hour -ge 6 ] && [ $hour -lt 18 ]; then
|
|
MODE="light"
|
|
WALLPAPER_DIR="Light"
|
|
else
|
|
MODE="dark"
|
|
WALLPAPER_DIR="Dark"
|
|
fi
|
|
```
|
|
|
|
### 7. NixOS Module Structure
|
|
|
|
**Module: `theme_switcher/default.nix`**
|
|
|
|
```nix
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
cfg = config.services.themeSwitcher;
|
|
in
|
|
{
|
|
options.services.themeSwitcher = {
|
|
enable = lib.mkEnableOption "dynamic theme switching based on time and wallpapers";
|
|
|
|
user = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Username for theme switching";
|
|
};
|
|
|
|
wallpaperPath = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "/home/${cfg.user}/nixos/shared/modules/services/wallpapers";
|
|
description = "Path to wallpaper directories (Light/ and Dark/)";
|
|
};
|
|
|
|
backend = lib.mkOption {
|
|
type = lib.types.enum [ "haishoku" "modern_colorthief" "fast_colorthief" ];
|
|
default = "haishoku";
|
|
description = "Pywal color extraction backend";
|
|
};
|
|
|
|
lightTime = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "06:00:00";
|
|
description = "Time to switch to light theme (HH:MM:SS)";
|
|
};
|
|
|
|
darkTime = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "18:00:00";
|
|
description = "Time to switch to dark theme (HH:MM:SS)";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
home-manager.users.${cfg.user} = {
|
|
home.packages = with pkgs; [
|
|
pywal16
|
|
python3Packages.haishoku
|
|
];
|
|
|
|
# Install theme application scripts
|
|
home.file.".local/bin/apply-theme.sh" = {
|
|
source = ./scripts/apply-theme.sh;
|
|
executable = true;
|
|
};
|
|
|
|
# Pywal custom templates for GTK and Kvantum
|
|
xdg.configFile."wal/templates/gtk-3.0.css".source = ./templates/gtk-3.0.css;
|
|
xdg.configFile."wal/templates/gtk-4.0.css".source = ./templates/gtk-4.0.css;
|
|
xdg.configFile."wal/templates/kvantum.kvconfig".source = ./templates/kvantum.kvconfig;
|
|
|
|
# Systemd timer and service
|
|
systemd.user.timers.theme-switcher = { /* ... */ };
|
|
systemd.user.services.theme-switcher = { /* ... */ };
|
|
|
|
# Keep Qt/GTK enabled but don't set static themes
|
|
qt = {
|
|
enable = true;
|
|
platformTheme.name = "kvantum";
|
|
style.name = "kvantum";
|
|
};
|
|
|
|
gtk.enable = true;
|
|
};
|
|
};
|
|
}
|
|
```
|
|
|
|
**Template files needed:**
|
|
- `templates/gtk-3.0.css` - GTK3 theme using pywal variables
|
|
- `templates/gtk-4.0.css` - GTK4 theme using pywal variables
|
|
- `templates/kvantum.kvconfig` - Kvantum config using pywal colors
|
|
|
|
## Testing & Validation
|
|
|
|
### 8. Test Plan
|
|
|
|
1. **Test pywal color extraction:**
|
|
```bash
|
|
# Test with light wallpaper
|
|
wal -i ~/nixos/shared/modules/services/wallpapers/Light/IU-Light.jpg --backend haishoku -l -n
|
|
cat ~/.cache/wal/colors.json # Verify colors extracted
|
|
|
|
# Test with dark wallpaper
|
|
wal -i ~/nixos/shared/modules/services/wallpapers/Dark/IU-Dark.jpg --backend haishoku -n
|
|
cat ~/.cache/wal/colors.json # Verify colors extracted
|
|
```
|
|
|
|
2. **Verify pywal template output:**
|
|
```bash
|
|
ls -la ~/.cache/wal/
|
|
# Should see: colors.json, ghostty.conf, colors.css, and custom templates
|
|
cat ~/.cache/wal/ghostty.conf # Check ghostty colors
|
|
```
|
|
|
|
3. **Manual theme application:**
|
|
```bash
|
|
~/.local/bin/apply-theme.sh --light
|
|
# Check: GTK apps, Qt apps, Ghostty colors update
|
|
|
|
~/.local/bin/apply-theme.sh --dark
|
|
# Check: Theme switches to dark
|
|
```
|
|
|
|
4. **Verify theme files:**
|
|
- `~/.cache/wal/colors.json` - Generated by pywal
|
|
- `~/.cache/wal/gtk-3.0.css` - From custom template
|
|
- `~/.cache/wal/gtk-4.0.css` - From custom template
|
|
- `~/.cache/wal/kvantum.kvconfig` - From custom template
|
|
- `~/.cache/wal/ghostty.conf` - From built-in template
|
|
|
|
5. **Test Kvantum theme:**
|
|
```bash
|
|
kvantummanager --set PywalTheme
|
|
# Or check: cat ~/.config/Kvantum/kvantum.kvconfig
|
|
```
|
|
|
|
6. **Test systemd automation:**
|
|
```bash
|
|
systemctl --user start theme-switcher.service
|
|
systemctl --user status theme-switcher.timer
|
|
systemctl --user list-timers | grep theme
|
|
```
|
|
|
|
7. **Test with live applications:**
|
|
- Open GTK app (nautilus, gnome-calculator) - verify colors
|
|
- Open Qt app (if available) - verify Kvantum theme
|
|
- Open Ghostty terminal - verify color palette matches wallpaper
|
|
- Check waybar/rofi if using pywal templates for those
|
|
|
|
## Implementation Steps Summary
|
|
|
|
### Phase 1: Setup pywal with existing wallpapers
|
|
1. Create theme_switcher module with pywal16 package
|
|
2. Test color extraction on Light/ and Dark/ wallpapers
|
|
3. Verify built-in templates (ghostty.conf, colors.css) generate correctly
|
|
|
|
### Phase 2: Create custom templates
|
|
1. Create `templates/gtk-3.0.css` using pywal variables
|
|
2. Create `templates/gtk-4.0.css` using pywal variables
|
|
3. Create `templates/kvantum.kvconfig` mapping colors.json to Kvantum format
|
|
4. Install templates to `~/.config/wal/templates/`
|
|
|
|
### Phase 3: Theme application script
|
|
1. Write `apply-theme.sh` script that:
|
|
- Runs pywal with appropriate flags (-l for light)
|
|
- Symlinks generated CSS to GTK config directories
|
|
- Copies Kvantum config to `~/.config/Kvantum/PywalTheme/`
|
|
- Runs `kvantummanager --set PywalTheme`
|
|
- Updates gsettings for color-scheme preference
|
|
|
|
### Phase 4: Time-based automation
|
|
1. Create systemd timer for light/dark switching times
|
|
2. Create systemd service that calls apply-theme.sh
|
|
3. Script determines mode based on current time
|
|
4. Selects random wallpaper from Light/ or Dark/ directory
|
|
|
|
### Phase 5: Integration and cleanup
|
|
1. Modify existing home.nix files to disable static themes when module enabled
|
|
2. Keep icon themes, cursor themes (not wallpaper-dependent)
|
|
3. Test on one host before deploying to all
|
|
4. Document wpaperd integration approach for future enhancement
|
|
|
|
## Key Technical Details
|
|
|
|
**Pywal template syntax:**
|
|
```
|
|
{background} -> #081212
|
|
{foreground} -> #c1c3c3
|
|
{color0} -> #081212
|
|
...
|
|
{color15} -> #c1c3c3
|
|
{cursor} -> #c1c3c3
|
|
{wallpaper} -> /path/to/wallpaper.jpg
|
|
```
|
|
|
|
**Template location:** `~/.config/wal/templates/filename.ext`
|
|
**Output location:** `~/.cache/wal/filename.ext`
|
|
|
|
**Ghostty:** Automatically reloads config when files change - no manual reload needed
|
|
|
|
**GTK:** Changes propagate via gsettings/dconf - running apps update automatically
|
|
|
|
**Kvantum:** May require app restart to see changes (Qt limitation)
|
|
|
|
**Wallpaper directories:**
|
|
- Light: `/home/nate/nixos/shared/modules/services/wallpapers/Light/`
|
|
- Dark: `/home/nate/nixos/shared/modules/services/wallpapers/Dark/`
|