update
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# Spec: theme-colors-sources
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Swaync reads colors from the active theme dir, not from Nix
|
||||
|
||||
The swaync Home Manager module SHALL NOT inline color strings. Instead, the module SHALL ensure that `~/.config/swaync/style.css` (and any related color file) is sourced from the active theme dir via an out-of-store symlink to `~/.config/colorschemes/.active/swaync/`.
|
||||
|
||||
#### Scenario: swaync config is symlinked to active theme
|
||||
- **WHEN** the user has applied this change
|
||||
- **THEN** `~/.config/swaync/colors.css` resolves through `~/.config/colorschemes/.active/swaync/colors.css`
|
||||
- **AND** the swaync Nix module contains no color hex strings
|
||||
|
||||
#### Scenario: Theme switch reflects in swaync without rebuild
|
||||
- **WHEN** the user runs `theme-switch catppuccin`
|
||||
- **THEN** swaync restyles via `swaync-client --reload-css` after the symlink swap
|
||||
- **AND** no `home-manager switch` is required
|
||||
|
||||
### Requirement: Wlogout reads colors from the active theme dir, not from Nix
|
||||
|
||||
The wlogout Home Manager module SHALL NOT inline color strings. Instead, the module SHALL ensure that `~/.config/wlogout/style.css` is sourced from the active theme dir via an out-of-store symlink to `~/.config/colorschemes/.active/wlogout/`.
|
||||
|
||||
#### Scenario: wlogout config is symlinked to active theme
|
||||
- **WHEN** the user has applied this change
|
||||
- **THEN** `~/.config/wlogout/style.css` resolves through `~/.config/colorschemes/.active/wlogout/style.css`
|
||||
- **AND** the wlogout Nix module contains no color hex strings
|
||||
|
||||
#### Scenario: Next wlogout invocation uses new colors
|
||||
- **WHEN** the user runs `theme-switch` and then invokes wlogout
|
||||
- **THEN** wlogout uses the new palette (no live reload required; wlogout reads its config at launch)
|
||||
|
||||
### Requirement: Alacritty imports colors from an external file
|
||||
|
||||
The alacritty Home Manager module SHALL configure alacritty to import colors from `~/.config/alacritty/colors.toml` (or equivalent), which is symlinked from the active theme dir.
|
||||
|
||||
#### Scenario: alacritty config imports external colors file
|
||||
- **WHEN** the user has applied this change
|
||||
- **THEN** `~/.config/alacritty/alacritty.toml` contains an `import = [...]` directive that includes the colors file
|
||||
- **AND** `~/.config/alacritty/colors.toml` resolves through `~/.config/colorschemes/.active/alacritty/colors.toml`
|
||||
- **AND** the alacritty Nix module contains no color hex strings (font, opacity, padding remain in Nix)
|
||||
|
||||
#### Scenario: Next alacritty launch uses new colors
|
||||
- **WHEN** the user runs `theme-switch` and then launches a new alacritty window
|
||||
- **THEN** the new window uses the new palette
|
||||
|
||||
### Requirement: Darkman is narrowed to hyprsunset only
|
||||
|
||||
The darkman Home Manager module SHALL retain its `hyprsunset` script (sunset/sunrise temperature) and SHALL NOT contain any `gtk-theme` script. Theme-driven dark/light preference is owned by `theme-switch` via gsettings, not by darkman.
|
||||
|
||||
#### Scenario: darkman scripts contain no gtk-theme step
|
||||
- **WHEN** the user has applied this change
|
||||
- **THEN** `services.darkman.darkModeScripts` does not include a `gtk-theme` key
|
||||
- **AND** `services.darkman.lightModeScripts` does not include a `gtk-theme` key
|
||||
|
||||
#### Scenario: darkman still drives hyprsunset
|
||||
- **WHEN** the time crosses the darkman sunset/sunrise boundary
|
||||
- **THEN** the `hyprsunset` script runs and adjusts temperature/gamma
|
||||
- **AND** gsettings `color-scheme` is NOT modified by darkman
|
||||
|
||||
### Requirement: Hyprland sources its colors from the active theme dir
|
||||
|
||||
The hyprland Home Manager module (or its user-facing config file) SHALL be configured so that hyprland's color directives come from a `source =` directive pointing at `~/.config/colorschemes/.active/hypr/colors.conf`. The Nix module SHALL NOT inline color hex strings for theme-affected values (active/inactive borders, etc.).
|
||||
|
||||
#### Scenario: Hyprland config sources from active theme
|
||||
- **WHEN** the user has applied this change
|
||||
- **THEN** `~/.config/hypr/hyprland.conf` (or its lua-generated form) contains `source = ~/.config/colorschemes/.active/hypr/colors.conf`
|
||||
- **AND** the Hyprland module contains no inline color hex strings for theme-affected directives
|
||||
|
||||
#### Scenario: Theme switch reflects in hyprland without rebuild
|
||||
- **WHEN** the user runs `theme-switch`
|
||||
- **THEN** hyprland automatically reloads its config (it watches the file)
|
||||
- **AND** no `home-manager switch` is required
|
||||
|
||||
### Requirement: Every themed app reads from a single source of truth
|
||||
|
||||
Every app whose colors are themed on this host SHALL read its palette from `~/.config/colorschemes/.active/<app>/...` (directly or via symlink). No themed app SHALL have its colors baked into a Nix-managed file.
|
||||
|
||||
#### Scenario: Audit: no color hex strings in themed-app Nix modules
|
||||
- **WHEN** the user runs `rg -i '#[0-9a-f]{6}' modules/home-manager/{swaync,wlogout,alacritty,hyprland,darkman}` after applying this change
|
||||
- **THEN** the only matches are in non-theme-affected contexts (e.g., alacritty font color is fine; swaync/wlogout/hyprland theme colors are gone)
|
||||
|
||||
#### Scenario: Adding a new theme requires no Nix change
|
||||
- **WHEN** the user creates a new `~/.config/colorschemes/<new-theme>/` directory with the same per-app subdirs as existing themes, plus a `meta` file
|
||||
- **THEN** `theme-switch <new-theme>` works without any `home-manager switch`
|
||||
- **AND** the new theme appears in the rofi picker on next invocation
|
||||
@@ -0,0 +1,155 @@
|
||||
# Spec: theme-switch
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Atomic theme swap via a single canonical pointer
|
||||
|
||||
The system SHALL provide a single shell command, `theme-switch <theme-name>`, that switches the active theme by atomically rewriting `~/.config/colorschemes/.active` (a symlink) to point at `~/.config/colorschemes/<theme-name>/`.
|
||||
|
||||
#### Scenario: Successful switch to a valid theme
|
||||
- **WHEN** the user runs `theme-switch catppuccin` and `~/.config/colorschemes/catppuccin/` exists
|
||||
- **THEN** `~/.config/colorschemes/.active` is a symlink whose target resolves to `~/.config/colorschemes/catppuccin`
|
||||
- **AND** `~/.config/colorschemes/.current` contains the text `catppuccin`
|
||||
|
||||
#### Scenario: Switch with an unknown theme name
|
||||
- **WHEN** the user runs `theme-switch does-not-exist` and the directory does not exist
|
||||
- **THEN** the command exits non-zero
|
||||
- **AND** prints a list of valid theme names to stderr
|
||||
- **AND** does not modify `.active` or `.current`
|
||||
|
||||
#### Scenario: Re-applying the current theme is a no-op
|
||||
- **WHEN** the user runs `theme-switch` with no arguments
|
||||
- **THEN** the command reads `.current`
|
||||
- **AND** re-applies every per-app reload step (idempotent)
|
||||
|
||||
### Requirement: Per-theme metadata drives picker and gsettings
|
||||
|
||||
The system SHALL read each theme's mode (dark or light) and display name from a `meta` file at `~/.config/colorschemes/<theme>/meta`.
|
||||
|
||||
#### Scenario: meta file format
|
||||
- **WHEN** the user runs `theme-switch` against any theme
|
||||
- **THEN** the script reads `<theme>/meta`
|
||||
- **AND** parses lines of the form `key=value`
|
||||
- **AND** uses `mode=dark` or `mode=light` to drive gsettings
|
||||
- **AND** uses `name=...` for the rofi picker label
|
||||
|
||||
#### Scenario: Missing meta file defaults to dark
|
||||
- **WHEN** a theme directory has no `meta` file
|
||||
- **THEN** `theme-switch` treats the theme as `mode=dark`
|
||||
- **AND** uses the directory name as the display name
|
||||
- **AND** emits a warning to stderr
|
||||
|
||||
### Requirement: Per-app live reload covers every themed app on jpporta-nixos
|
||||
|
||||
`theme-switch` SHALL emit the appropriate reload signal for each themed app on the host, in an order that places file/data changes before signals.
|
||||
|
||||
#### Scenario: Hyprland picks up new colors via file watch
|
||||
- **WHEN** `theme-switch` rewrites `~/.config/hypr/hyprland.conf` so that its `source =` directive points at `~/.config/colorschemes/.active/hypr/colors.conf`
|
||||
- **THEN** the running Hyprland instance reloads automatically (it watches its config file)
|
||||
|
||||
#### Scenario: waybar picks up new colors via SIGUSR2
|
||||
- **WHEN** `theme-switch` swaps the waybar CSS symlink and the colors.css file under it
|
||||
- **THEN** the running waybar instance receives SIGUSR2
|
||||
- **AND** restyles with the new palette
|
||||
|
||||
#### Scenario: swaync picks up new CSS via swaync-client
|
||||
- **WHEN** `theme-switch` swaps `~/.config/swaync/colors.css`
|
||||
- **THEN** the script invokes `swaync-client --reload-css`
|
||||
- **AND** swaync restyles without restart
|
||||
|
||||
#### Scenario: kitty picks up new colors via IPC
|
||||
- **WHEN** `theme-switch` invokes `kitty @ set-colors --all ~/.config/colorschemes/.active/kitty/colors.conf`
|
||||
- **THEN** every running kitty window restyles without restart
|
||||
|
||||
#### Scenario: ghostty picks up new colors via SIGHUP
|
||||
- **WHEN** `theme-switch` sends SIGHUP to the running ghostty process
|
||||
- **THEN** ghostty re-reads its config (including the colors block)
|
||||
|
||||
#### Scenario: alacritty uses the new palette on next launch
|
||||
- **WHEN** `theme-switch` swaps the alacritty colors file that `alacritty.toml` imports
|
||||
- **THEN** running alacritty windows keep their current colors until restarted
|
||||
- **AND** any newly launched alacritty instance uses the new palette
|
||||
|
||||
#### Scenario: nvim recolors in-session when $NVIM is set
|
||||
- **WHEN** `theme-switch` runs and the user has `$NVIM` set (a running nvim server)
|
||||
- **THEN** the script invokes `nvim --remote-expr 'lua vim.cmd.colorscheme("<name>")'`
|
||||
- **AND** the running nvim recolors immediately
|
||||
- **WHEN** `$NVIM` is not set
|
||||
- **THEN** the script takes no action for nvim
|
||||
- **AND** newly launched nvim instances pick up the theme from the symlinked config
|
||||
|
||||
#### Scenario: GTK dark/light preference follows theme meta
|
||||
- **WHEN** `theme-switch` reads `mode=dark` from the new theme's meta
|
||||
- **THEN** the script invokes `gsettings set org.gnome.desktop.interface color-scheme prefer-dark`
|
||||
- **WHEN** `mode=light`
|
||||
- **THEN** the script invokes `gsettings set org.gnome.desktop.interface color-scheme prefer-light`
|
||||
|
||||
### Requirement: Rofi-driven theme picker is a thin wrapper over theme-switch
|
||||
|
||||
The system SHALL provide a `theme-picker` script that lists available themes (display name + mode) via `rofi -dmenu` and invokes `theme-switch` on the user's selection.
|
||||
|
||||
#### Scenario: Picker shows all available themes
|
||||
- **WHEN** the user runs `theme-picker`
|
||||
- **THEN** rofi displays one entry per theme directory under `~/.config/colorschemes/`
|
||||
- **AND** each entry shows the display name from `meta` and a `(dark)` or `(light)` suffix
|
||||
|
||||
#### Scenario: Picker invokes theme-switch on selection
|
||||
- **WHEN** the user selects an entry in rofi
|
||||
- **THEN** `theme-picker` invokes `theme-switch <selected-theme-name>`
|
||||
- **AND** exits with the same status code
|
||||
|
||||
### Requirement: Boot-time re-apply guarantees a consistent desktop on login
|
||||
|
||||
The system SHALL provide a systemd user oneshot service that runs `theme-switch` (no args) on graphical-session start, ensuring `.current` is applied before the user sees the desktop.
|
||||
|
||||
#### Scenario: Service runs on graphical-session start
|
||||
- **WHEN** the user's graphical session (Hyprland on jpporta-nixos; cage on jpporta-deck) starts
|
||||
- **THEN** `theme-apply.service` runs `theme-switch`
|
||||
- **AND** the service has no effect if `.current` already matches the running state
|
||||
|
||||
#### Scenario: Service does not run before graphical session
|
||||
- **WHEN** the user's session is not yet at graphical-session.target
|
||||
- **THEN** `theme-apply.service` is not started
|
||||
- **AND** waits for the target
|
||||
|
||||
### Requirement: Wallpaper hook is user-owned and exec'd at end of switch
|
||||
|
||||
`theme-switch` SHALL execute `~/.config/colorschemes/.hook <theme-name>` at the end of every successful switch if that file exists and is executable.
|
||||
|
||||
#### Scenario: Hook is invoked after all app reloads
|
||||
- **WHEN** `theme-switch` finishes the per-app reload block
|
||||
- **AND** `~/.config/colorschemes/.hook` exists and is executable
|
||||
- **THEN** the script executes `~/.config/colorschemes/.hook <theme-name>`
|
||||
- **AND** any non-zero exit status is logged but does not fail the switch
|
||||
|
||||
#### Scenario: Missing hook does not fail the switch
|
||||
- **WHEN** `~/.config/colorschemes/.hook` does not exist or is not executable
|
||||
- **THEN** the switch still completes successfully
|
||||
|
||||
### Requirement: Writer-deck adopts the same mechanism with foot handled at switch time
|
||||
|
||||
The same `theme-switch` script SHALL work on jpporta-deck (cage + foot). Foot has no live color reload, so the deck profile SHALL include a foot-specific step that sends `SIGUSR1` (footserver reload) or, as a fallback, restarts the foot server.
|
||||
|
||||
#### Scenario: Foot recolors via SIGUSR1 to footserver
|
||||
- **WHEN** `theme-switch` runs on jpporta-deck
|
||||
- **THEN** the script sends `SIGUSR1` to the footserver process if running
|
||||
- **AND** foot reloads its config without losing the running instance
|
||||
|
||||
#### Scenario: footserver not running
|
||||
- **WHEN** `theme-switch` runs on jpporta-deck and no footserver is running
|
||||
- **THEN** the script takes no action for foot
|
||||
- **AND** the next foot launch picks up the new theme
|
||||
|
||||
### Requirement: First-install migration creates .active from custom.theme.current
|
||||
|
||||
On a fresh `home-manager switch` after this change is applied, the theme module SHALL ensure `~/.config/colorschemes/.active` exists and points at the theme named in `custom.theme.current`.
|
||||
|
||||
#### Scenario: First install with default theme
|
||||
- **WHEN** the user applies this change for the first time and `custom.theme.current = "gruvbox-dark"`
|
||||
- **THEN** the activation creates `~/.config/colorschemes/.active` as a symlink to `~/.config/colorschemes/gruvbox-dark/`
|
||||
- **AND** writes `gruvbox-dark` to `~/.config/colorschemes/.current`
|
||||
|
||||
#### Scenario: Existing wallpapers symlink is left in place during migration
|
||||
- **WHEN** `~/.config/colorschemes/wallpapers` is already a symlink (legacy)
|
||||
- **THEN** the migration does not remove it
|
||||
- **AND** documents in a comment that it is now redundant
|
||||
Reference in New Issue
Block a user