refactor: major architectural restructure for theme-centric organization

Theme System:
- Move all theme app configs to apps/ subdirectory (20 themes)
- Add theme-loader.nix for dynamic theme config deployment
- Simplify stylix.nix to focus on base theming only

Override System:
- Add overrides.nix for file-based config overrides
- Add behavior-configs.nix for non-visual configuration
- Split hypr/nomarchy.conf into behavior vs visual sections

Module Improvements:
- Add lib.mkDefault to all customizable settings
- Add modules/lib/ with shared utilities and state schema
- Update all home and system modules for downstream overridability

Installer:
- New minimal TTY installer (installer/install.sh)
- Golden path: BTRFS + LUKS2 (disko-golden.nix)
- New installer-iso.nix for TTY-only installation
- Keep graphical installer as installerIsoGraphical option

Cleanup:
- Remove obsolete install.sh, disko-ext4.nix, install-nomarchy.sh
- Update live-iso.nix references
- Add .claude/ to .gitignore for local IDE settings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-04-11 19:38:27 +01:00
parent 769fd88f25
commit b27fc5aee8
141 changed files with 2014 additions and 943 deletions

View File

@@ -2,8 +2,8 @@
{
programs.alacritty = {
enable = true;
settings = {
enable = lib.mkDefault true;
settings = lib.mkDefault {
env = {
TERM = "xterm-256color";
};

View File

@@ -3,19 +3,17 @@
{
programs.bash = {
enable = true;
# Safely append user's custom RC file after NixOS setup
bashrcExtra = ''
if [[ -f ~/.config/nomarchy/default/bash/rc ]]; then
source ~/.config/nomarchy/default/bash/rc
fi
'';
# Import aliases from the static file logic
shellAliases = lib.mkDefault {
# File system
lsa = "ls -a";
# Directories
".." = "cd ..";
"..." = "cd ../..";
@@ -32,14 +30,10 @@
gcm = "git commit -m";
gcam = "git commit -a -m";
gcad = "git commit -a --amend";
# NixOS specific (inherited from default.nix but keeping here for consistency)
# NixOS commands
sys-update = "sudo nixos-rebuild switch --flake /etc/nixos#default --impure";
env-update = "nomarchy-preflight-migration && home-manager switch --flake /etc/nixos#default --impure";
};
};
# Ensure the directory exists in the user's home via xdg.configFile
# This mapping is likely already handled in configs.nix, but we ensure it here
# or in the main config mapping.
}

View File

@@ -0,0 +1,97 @@
{ config, lib, ... }:
# Behavior Configuration Module
#
# This module deploys non-visual configuration files (keybindings, input settings,
# window rules, etc.) with lib.mkDefault, allowing downstream users to override.
#
# Visual/theme configs are handled separately by theme-loader.nix and stylix.nix.
#
# Behavior configs include:
# - Keybindings (bindings, media keys, clipboard)
# - Input settings (keyboard, mouse, touchpad)
# - Window rules and layouts
# - Autostart applications
# - Environment variables
let
configDir = ../../config;
overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides";
# Check if user has an override for a specific config
hasOverride = path: builtins.pathExists "${overridesDir}/${path}";
# Behavior config categories with their source paths
behaviorConfigs = {
# Hyprland behavior (non-visual)
"nomarchy/default/hypr/bindings.conf" = "hypr/bindings.conf";
"nomarchy/default/hypr/bindings/media.conf" = "hypr/bindings/media.conf";
"nomarchy/default/hypr/bindings/clipboard.conf" = "hypr/bindings/clipboard.conf";
"nomarchy/default/hypr/bindings/tiling-v2.conf" = "hypr/bindings/tiling-v2.conf";
"nomarchy/default/hypr/bindings/utilities.conf" = "hypr/bindings/utilities.conf";
"nomarchy/default/hypr/input.conf" = "hypr/input.conf";
"nomarchy/default/hypr/windows.conf" = "hypr/windows.conf";
"nomarchy/default/hypr/autostart.conf" = "hypr/autostart.conf";
"nomarchy/default/hypr/envs.conf" = "hypr/envs.conf";
"nomarchy/default/hypr/looknfeel.conf" = "hypr/looknfeel.conf";
# App-specific window rules (behavior, not visual)
"nomarchy/default/hypr/apps.conf" = "hypr/apps.conf";
"nomarchy/default/hypr/apps/qemu.conf" = "hypr/apps/qemu.conf";
"nomarchy/default/hypr/apps/steam.conf" = "hypr/apps/steam.conf";
"nomarchy/default/hypr/apps/terminals.conf" = "hypr/apps/terminals.conf";
"nomarchy/default/hypr/apps/walker.conf" = "hypr/apps/walker.conf";
"nomarchy/default/hypr/apps/browser.conf" = "hypr/apps/browser.conf";
"nomarchy/default/hypr/apps/1password.conf" = "hypr/apps/1password.conf";
"nomarchy/default/hypr/apps/bitwarden.conf" = "hypr/apps/bitwarden.conf";
"nomarchy/default/hypr/apps/pip.conf" = "hypr/apps/pip.conf";
"nomarchy/default/hypr/apps/system.conf" = "hypr/apps/system.conf";
"nomarchy/default/hypr/apps/localsend.conf" = "hypr/apps/localsend.conf";
"nomarchy/default/hypr/apps/telegram.conf" = "hypr/apps/telegram.conf";
"nomarchy/default/hypr/apps/geforce.conf" = "hypr/apps/geforce.conf";
"nomarchy/default/hypr/apps/moonlight.conf" = "hypr/apps/moonlight.conf";
"nomarchy/default/hypr/apps/retroarch.conf" = "hypr/apps/retroarch.conf";
"nomarchy/default/hypr/apps/webcam-overlay.conf" = "hypr/apps/webcam-overlay.conf";
"nomarchy/default/hypr/apps/davinci-resolve.conf" = "hypr/apps/davinci-resolve.conf";
"nomarchy/default/hypr/apps/hyprshot.conf" = "hypr/apps/hyprshot.conf";
};
in
{
options.nomarchy.behavior = {
hyprland = {
bindings = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default Hyprland keybindings.";
};
input = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default input settings.";
};
windowRules = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default window rules.";
};
autostart = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default autostart configuration.";
};
};
};
config = {
# Note: The actual config deployment is handled by configs.nix
# This module provides the options and documentation for behavior configs
# The separation allows users to selectively disable behavior categories
# Ensure behavior config directories exist in overrides
home.activation.createBehaviorOverrideDirs = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p "${overridesDir}/hypr/bindings"
mkdir -p "${overridesDir}/hypr/apps"
'';
};
}

View File

@@ -2,54 +2,68 @@
let
configDir = ../../config;
# Read the contents of the upstream config directory
configEntries = builtins.readDir configDir;
# Explicit list of config items to manage
# This replaces dynamic builtins.readDir for clarity and faster evaluation
configItems = {
# Directories
btop = "directory";
chromium = "directory";
elephant = "directory";
"environment.d" = "directory";
fastfetch = "directory";
fcitx5 = "directory";
fontconfig = "directory";
ghostty = "directory";
git = "directory";
hypr = "directory";
"hyprland-preview-share-picker" = "directory";
imv = "directory";
kitty = "directory";
lazygit = "directory";
nomarchy = "directory";
opencode = "directory";
systemd = "directory";
tmux = "directory";
Typora = "directory";
uwsm = "directory";
wiremix = "directory";
xournalpp = "directory";
# Files
"brave-flags.conf" = "regular";
"chromium-flags.conf" = "regular";
"starship.toml" = "regular";
"xdg-terminals.list" = "regular";
};
# Files/directories handled elsewhere or not intended for ~/.config/
# - nomarchy.ttf: font file, not a config
# - waybar: handled in waybar.nix
# - walker: handled in walker.nix
# - alacritty: handled in alacritty.nix
# - swayosd: handled in swayosd.nix
# Check for user overrides
userConfigDir = config.nomarchy.configOverrides;
userEntries = if userConfigDir != null && builtins.pathExists userConfigDir
then builtins.readDir userConfigDir
else {};
# Files to explicitly exclude (handled elsewhere or not intended for ~/.config/)
excludedFiles = [
"nomarchy.ttf"
"waybar"
"walker"
"alacritty"
"swayosd"
];
# Get all unique names from both sources
allNames = lib.unique (builtins.attrNames configEntries ++ builtins.attrNames userEntries);
# Filter the entries
validEntries = builtins.filter (name: !(builtins.elem name excludedFiles)) allNames;
hasUserOverrides = userConfigDir != null && builtins.pathExists userConfigDir;
# Generate the xdg.configFile attribute set
# If a name exists in userEntries, it takes precedence.
# For directories, we use `recursive = true;` to allow the user to create their own files alongside the read-only defaults.
makeMapping = name: {
inherit name;
value = lib.mkDefault {
source = if userEntries ? ${name}
then "${userConfigDir}/${name}"
else "${configDir}/${name}";
recursive = (userEntries.${name} or configEntries.${name}) == "directory";
makeMapping = name: type:
let
hasUserOverride = hasUserOverrides && builtins.pathExists "${userConfigDir}/${name}";
source = if hasUserOverride then "${userConfigDir}/${name}" else "${configDir}/${name}";
in {
inherit name;
value = lib.mkDefault {
inherit source;
recursive = type == "directory";
};
};
};
configMappings = lib.mapAttrs' (name: type: makeMapping name type) configItems;
in
{
xdg.configFile = (builtins.listToAttrs (map makeMapping validEntries)) // {
"nomarchy" = {
source = ../../config/nomarchy;
recursive = true;
};
"hypr" = {
source = ../../config/hypr;
recursive = true;
};
};
xdg.configFile = configMappings;
}

View File

@@ -1,10 +1,10 @@
{ config, pkgs, inputs, lib, ... }:
let
palettes = import ../../assets/themes/nomarchy-palettes.nix;
nomarchyLib = import ../lib { inherit lib; };
userPackagesFile = "${config.home.homeDirectory}/.config/home-manager/user-packages.json";
userPackages = if builtins.pathExists userPackagesFile then
let
userPackages = if builtins.pathExists userPackagesFile then
let
pkgNames = builtins.fromJSON (builtins.readFile userPackagesFile);
# Filter to only packages that exist in pkgs to prevent build failures
validPkgs = builtins.filter (name: builtins.hasAttr name pkgs) pkgNames;
@@ -17,6 +17,9 @@ in
inputs.walker.homeManagerModules.default
./options.nix
./state.nix
./overrides.nix
./behavior-configs.nix
./theme-loader.nix
./theme-files.nix
./fonts.nix
./alacritty.nix
@@ -35,30 +38,47 @@ in
./bash.nix
];
colorScheme = lib.mkDefault (palettes.${config.nomarchy.theme} or palettes.nord);
colorScheme = lib.mkDefault (nomarchyLib.getColorScheme config.nomarchy.theme);
home.packages = lib.mkDefault (with pkgs; [
# Core applications
firefox
xfce.thunar
imv
mpv
neovim
# Media
imv # Image viewer
mpv # Video player
# Hyprland ecosystem
swww # Wallpaper daemon
mako # Notification daemon
hyprlock # Lock screen
# Screenshot and clipboard
wl-clipboard
grim
slurp
# Hardware control
brightnessctl
playerctl
pamixer
mise
# Utilities
jq
xmlstarlet
mise
gum # TUI components for scripts
# Theming
yaru-theme
bibata-cursors
# Fonts
nerd-fonts.jetbrains-mono
nerd-fonts.roboto-mono
nerd-fonts.fira-code
nerd-fonts.ubuntu-mono
] ++ userPackages);
# Shell aliases are now managed in bash.nix
}

View File

@@ -1,8 +1,8 @@
{ ... }:
{ lib, ... }:
{
config = {
fonts.fontconfig.enable = true;
xdg.dataFile."fonts/nomarchy.ttf".source = ../../config/nomarchy.ttf;
fonts.fontconfig.enable = lib.mkDefault true;
xdg.dataFile."fonts/nomarchy.ttf".source = lib.mkDefault ../../config/nomarchy.ttf;
};
}

View File

@@ -1,20 +1,27 @@
{ config, pkgs, lib, ... }:
let
activeWallpaper = if config.nomarchy.wallpaper != "" then
config.nomarchy.wallpaper
else "${../../assets/themes/catppuccin/backgrounds/1-totoro.png}";
nomarchyLib = import ../lib { inherit lib; };
assetsPath = ../../assets/themes;
# Use shared wallpaper resolver
activeWallpaper = nomarchyLib.resolveWallpaper {
wallpaperPath = config.nomarchy.wallpaper;
themeName = config.nomarchy.theme;
inherit assetsPath;
};
hyprlandState = config.nomarchy.hyprland;
in
{
home.sessionVariables = {
home.sessionVariables = lib.mkDefault {
WLR_NO_HARDWARE_CURSORS = "1";
HYPRLAND_LOG_WLR = "1";
};
wayland.windowManager.hyprland = {
enable = true;
settings = {
enable = lib.mkDefault true;
settings = lib.mkDefault {
"debug" = {
"disable_logs" = false;
"enable_stdout_logs" = true;
@@ -30,7 +37,7 @@ in
"col.inactive_border" = "rgb(${config.colorScheme.palette.base03})";
};
};
extraConfig = ''
extraConfig = lib.mkDefault ''
source = ~/.config/hypr/nomarchy.conf
'';
};

View File

@@ -1,9 +1,9 @@
{ config, ... }:
{ config, lib, ... }:
{
services.hypridle = {
enable = config.nomarchy.toggles.idle;
settings = {
enable = lib.mkDefault config.nomarchy.toggles.idle;
settings = lib.mkDefault {
general = {
lock_cmd = "nomarchy-lock-screen";
before_sleep_cmd = "loginctl lock-session";

View File

@@ -1,11 +1,11 @@
{ config, ... }:
{ config, lib, ... }:
let
temp = toString (if config.nomarchy.toggles.nightlight then config.nomarchy.nightlightTemperature else 6500);
in
{
services.hyprsunset = {
enable = true; # Always enabled, we control via IPC and state
extraArgs = [ "--temperature" temp ];
enable = lib.mkDefault true; # Always enabled, we control via IPC and state
extraArgs = lib.mkDefault [ "--temperature" temp ];
};
}

102
modules/home/overrides.nix Normal file
View File

@@ -0,0 +1,102 @@
{ config, lib, ... }:
# File-based override system for Nomarchy
#
# Users can place config files in ~/.config/nomarchy/overrides/ to completely
# replace upstream defaults. Override priority (highest to lowest):
# 1. User Nix options
# 2. User file overrides (~/.config/nomarchy/overrides/)
# 3. Upstream defaults
#
# Supported override paths:
# - hypr/ - Hyprland configs (bindings.conf, input.conf, etc.)
# - waybar/ - Waybar config and style
# - alacritty/ - Alacritty terminal config
# - walker/ - Walker launcher config
# - kitty/ - Kitty terminal config
# - btop/ - Btop resource monitor config
# - apps/ - Other application configs
let
overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides";
# Check if a specific override exists
hasOverride = path: builtins.pathExists "${overridesDir}/${path}";
# Get override source if it exists, otherwise use default
getOverrideOrDefault = { path, default }:
if hasOverride path
then "${overridesDir}/${path}"
else default;
in
{
options.nomarchy.overrides = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to enable file-based override loading from ~/.config/nomarchy/overrides/";
};
paths = lib.mkOption {
type = lib.types.attrsOf lib.types.path;
default = {};
description = "Override paths discovered at build time. Populated by the override system.";
};
};
config = lib.mkIf config.nomarchy.overrides.enable {
# Create the overrides directory structure if it doesn't exist
home.activation.createOverridesDir = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p "${overridesDir}"
mkdir -p "${overridesDir}/hypr"
mkdir -p "${overridesDir}/waybar"
mkdir -p "${overridesDir}/apps"
'';
# Document the override system
xdg.configFile."nomarchy/overrides/README.md".text = lib.mkDefault ''
# Nomarchy Configuration Overrides
Place files in this directory to override upstream Nomarchy defaults.
## Directory Structure
```
overrides/
hypr/
bindings.conf # Keybindings
input.conf # Input settings
monitors.conf # Monitor layout
rules.conf # Window rules
autostart.conf # Startup apps
waybar/
config.jsonc # Waybar layout
style.css # Waybar styling
apps/
alacritty.toml # Terminal behavior
...
README.md # This file
```
## Override Priority
1. **Nix Options** (highest) - Set in your flake/config
2. **File Overrides** - Files in this directory
3. **Upstream Defaults** (lowest) - Nomarchy defaults
## Usage
1. Copy the file you want to customize from the upstream config
2. Place it in the appropriate directory here
3. Edit to your preferences
4. Run `nixos-rebuild switch` or `home-manager switch`
## Tips
- For keybindings, copy `~/.config/hypr/bindings.conf` to `overrides/hypr/`
- For Waybar styling, copy `~/.config/waybar/style.css` to `overrides/waybar/`
- Changes here persist across Nomarchy updates
'';
};
}

View File

@@ -1,62 +1,151 @@
{ config, pkgs, lib, ... }:
let
# Core dependencies needed by most Nomarchy scripts
nomarchy-deps = with pkgs; [
gum
hyprland
procps
libnotify
# Core dependencies needed by most scripts (minimal set)
coreDeps = with pkgs; [
coreutils
gnused
gnugrep
pciutils
findutils
jq
swww
xmlstarlet
wl-clipboard
grim
slurp
brightnessctl
playerctl
pamixer
pulseaudio
wireplumber
swayosd
gawk
curl
wget
# Add any others commonly used in bin/
jq
];
# Category-specific dependencies
categoryDeps = with pkgs; {
appearance = [
swww
libnotify
];
apps = [
hyprland
libnotify
];
hardware = [
brightnessctl
playerctl
pamixer
pciutils
libnotify
];
system = [
gum
libnotify
curl
wget
];
utils = [
gum
hyprland
libnotify
wl-clipboard
grim
slurp
xmlstarlet
curl
wget
];
wm = [
hyprland
swayosd
libnotify
procps
];
};
# All dependencies combined (for scripts that might call others)
allDeps = coreDeps ++ (lib.unique (lib.flatten (builtins.attrValues categoryDeps)));
# Environment variables to inject
envVars = {
NOMARCHY_TOGGLE_SUSPEND = if config.nomarchy.toggles.suspend then "true" else "false";
NOMARCHY_TOGGLE_SCREENSAVER = if config.nomarchy.toggles.screensaver then "true" else "false";
NOMARCHY_TOGGLE_IDLE = if config.nomarchy.toggles.idle then "true" else "false";
NOMARCHY_TOGGLE_NIGHTLIGHT = if config.nomarchy.toggles.nightlight then "true" else "false";
NOMARCHY_TOGGLE_WAYBAR = if config.nomarchy.toggles.waybar then "true" else "false";
NOMARCHY_TOGGLE_SKIP_VSCODE_THEME = if config.nomarchy.toggles.skipVsCodeTheme then "true" else "false";
NOMARCHY_MONOSPACE_FONT = config.nomarchy.fonts.monospace;
};
# Build wrapper arguments for environment variables
envWrapperArgs = lib.concatStringsSep " " (
lib.mapAttrsToList (name: value: "--set ${name} \"${value}\"") envVars
);
nomarchy-scripts = pkgs.stdenv.mkDerivation {
pname = "nomarchy-scripts";
version = "1.0.0";
src = ../../bin;
nativeBuildInputs = [ pkgs.makeWrapper ];
installPhase = ''
mkdir -p $out/bin
find . -type f -exec cp {} $out/bin/ \;
# Copy scripts preserving directory structure for category detection
for category in appearance apps hardware system utils wm; do
if [ -d "$category" ]; then
for script in "$category"/*; do
if [ -f "$script" ]; then
cp "$script" "$out/bin/"
fi
done
fi
done
chmod +x $out/bin/*
patchShebangs $out/bin
'';
postFixup = ''
# Wrap every script to ensure dependencies are in PATH and inject configuration
# Wrap scripts with category-specific dependencies
for file in $out/bin/*; do
if [ -f "$file" ]; then
scriptName=$(basename "$file")
# Determine category from original source location
category=""
for cat in appearance apps hardware system utils wm; do
if [ -f "$src/$cat/$scriptName" ]; then
category="$cat"
break
fi
done
# Select dependencies based on category
case "$category" in
appearance)
deps="${lib.makeBinPath (coreDeps ++ categoryDeps.appearance)}"
;;
apps)
deps="${lib.makeBinPath (coreDeps ++ categoryDeps.apps)}"
;;
hardware)
deps="${lib.makeBinPath (coreDeps ++ categoryDeps.hardware)}"
;;
system)
deps="${lib.makeBinPath (coreDeps ++ categoryDeps.system)}"
;;
utils)
deps="${lib.makeBinPath (coreDeps ++ categoryDeps.utils)}"
;;
wm)
deps="${lib.makeBinPath (coreDeps ++ categoryDeps.wm)}"
;;
*)
# Fallback to all deps for unknown categories
deps="${lib.makeBinPath allDeps}"
;;
esac
wrapProgram "$file" \
--prefix PATH : ${lib.makeBinPath nomarchy-deps} \
--set NOMARCHY_TOGGLE_SUSPEND "${if config.nomarchy.toggles.suspend then "true" else "false"}" \
--set NOMARCHY_TOGGLE_SCREENSAVER "${if config.nomarchy.toggles.screensaver then "true" else "false"}" \
--set NOMARCHY_TOGGLE_IDLE "${if config.nomarchy.toggles.idle then "true" else "false"}" \
--set NOMARCHY_TOGGLE_NIGHTLIGHT "${if config.nomarchy.toggles.nightlight then "true" else "false"}" \
--set NOMARCHY_TOGGLE_WAYBAR "${if config.nomarchy.toggles.waybar then "true" else "false"}" \
--set NOMARCHY_TOGGLE_SKIP_VSCODE_THEME "${if config.nomarchy.toggles.skipVsCodeTheme then "true" else "false"}" \
--set NOMARCHY_MONOSPACE_FONT "${config.nomarchy.fonts.monospace}"
--prefix PATH : "$deps" \
${envWrapperArgs}
fi
done
'';

View File

@@ -1,24 +1,11 @@
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
let
stateDir = "${config.home.homeDirectory}/.config/home-manager";
# Helper to read state from a file, with a default
readState = file: default:
if builtins.pathExists "${stateDir}/${file}" then
let
content = builtins.readFile "${stateDir}/${file}";
cleanContent = lib.removeSuffix "\n" content;
in
if lib.hasSuffix ".json" file then
builtins.fromJSON cleanContent
else
cleanContent
else
default;
nomarchyLib = import ../lib { inherit lib; };
assetsPath = ../../assets/themes;
# Unified state reading
togglesState = readState "state.json" {};
# Read unified state from ~/.config/nomarchy/state.json
togglesState = nomarchyLib.readHomeState config.home.homeDirectory;
in
{
config = {
@@ -40,15 +27,16 @@ in
border_size = togglesState.hyprland.border_size or 2;
};
fonts.monospace = togglesState.font or "JetBrainsMono Nerd Font";
# Derived properties from the theme directory
isLightMode = builtins.pathExists (../../assets/themes + "/${togglesState.theme or "nord"}/light.mode");
iconsTheme = let
iconsFile = ../../assets/themes + "/${togglesState.theme or "nord"}/icons.theme";
in
if builtins.pathExists iconsFile
then lib.removeSuffix "\n" (builtins.readFile iconsFile)
else "Yaru-blue";
isLightMode = nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or "nord";
inherit assetsPath;
};
iconsTheme = nomarchyLib.getIconsTheme {
themeName = togglesState.theme or "nord";
inherit assetsPath;
};
};
};
}

View File

@@ -1,37 +1,57 @@
{ config, pkgs, inputs, lib, ... }:
let
# Re-use our state-based logic
palettes = import ../../assets/themes/nomarchy-palettes.nix;
activeThemeName = config.nomarchy.theme;
activeWallpaper = if config.nomarchy.wallpaper != "" then
# Use the absolute path from the state file (string format to avoid Nix Store copy)
config.nomarchy.wallpaper
else ../../assets/themes/catppuccin/backgrounds/1-totoro.png; # Fallback path object (copied to store)
# Stylix Integration Module
#
# This module handles base-level theming through Stylix:
# - Color scheme injection from the active theme's palette
# - Cursor configuration
# - Font configuration
# - GTK/GNOME theming
#
# App-specific theming is handled separately:
# - theme-loader.nix: Deploys theme's apps/ configs (btop, neovim, etc.)
# - waybar.nix: Generates waybar CSS from colorScheme
# - hyprland.nix: Handles hyprland border colors
#
# Stylix targets disabled here (we have custom implementations):
# - hyprland: Custom border/rule config
# - waybar: Custom CSS with theme colors
# Map nix-colors palette to a format Stylix expects (attrset of hex strings)
currentPalette = (palettes.${activeThemeName} or palettes.nord).palette;
let
nomarchyLib = import ../lib { inherit lib; };
assetsPath = ../../assets/themes;
activeThemeName = config.nomarchy.theme;
# Use shared wallpaper resolver
activeWallpaper = nomarchyLib.resolveWallpaper {
wallpaperPath = config.nomarchy.wallpaper;
themeName = activeThemeName;
inherit assetsPath;
};
# Get palette using shared library
currentPalette = nomarchyLib.getPalette activeThemeName;
in
{
imports = [ inputs.stylix.homeModules.stylix ];
stylix = {
enable = true;
enableReleaseChecks = false;
image = activeWallpaper;
base16Scheme = currentPalette;
# Use detected light mode state
polarity = if config.nomarchy.isLightMode then "light" else "dark";
enable = lib.mkDefault true;
enableReleaseChecks = lib.mkDefault false;
image = lib.mkDefault activeWallpaper;
base16Scheme = lib.mkDefault currentPalette;
cursor = {
# Use detected light mode state
polarity = lib.mkDefault (if config.nomarchy.isLightMode then "light" else "dark");
cursor = lib.mkDefault {
package = config.nomarchy.cursor.package;
name = config.nomarchy.cursor.name;
size = 24;
};
fonts = {
fonts = lib.mkDefault {
monospace = {
package = pkgs.nerd-fonts.jetbrains-mono;
name = config.nomarchy.fonts.monospace;
@@ -57,7 +77,7 @@ in
};
# Enable theming for specific targets
targets = {
targets = lib.mkDefault {
hyprland.enable = false; # We keep our custom hyprland config for borders/rules
waybar.enable = false; # We keep our custom waybar CSS
alacritty.enable = true;
@@ -69,8 +89,8 @@ in
# GTK Icon Theme configuration
gtk = {
enable = true;
iconTheme = {
enable = lib.mkDefault true;
iconTheme = lib.mkDefault {
package = pkgs.yaru-theme;
name = config.nomarchy.iconsTheme;
};

View File

@@ -1,12 +1,12 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:
{
services.swayosd = {
enable = true;
stylePath = "${config.home.homeDirectory}/.config/swayosd/style.css";
enable = lib.mkDefault true;
stylePath = lib.mkDefault "${config.home.homeDirectory}/.config/swayosd/style.css";
};
xdg.configFile."swayosd/style.css".text = ''
xdg.configFile."swayosd/style.css".text = lib.mkDefault ''
@define-color background-color #${config.colorScheme.palette.base00};
@define-color border-color #${config.colorScheme.palette.base0E};
@define-color label #${config.colorScheme.palette.base05};

View File

@@ -1,14 +1,31 @@
{ config, lib, ... }:
let
themePath = ../../assets/themes + "/${config.nomarchy.theme}";
themeAppsPath = themePath + "/apps";
nordAppsPath = ../../assets/themes/nord/apps;
# Check if theme has apps/hyprland.conf
hasHyprlandConf = builtins.pathExists (themeAppsPath + "/hyprland.conf");
in
{
xdg.configFile."nomarchy/current/theme" = {
source = ../../assets/themes/${config.nomarchy.theme};
source = themePath;
recursive = true;
};
# Ensure theme-specific hyprland config exists, fallback to nord if not
xdg.configFile."nomarchy/current/theme/hyprland.conf" = lib.mkIf (! builtins.pathExists (../../assets/themes + "/${config.nomarchy.theme}/hyprland.conf")) {
source = ../../assets/themes/nord/hyprland.conf;
# Now checking in apps/ subdirectory
xdg.configFile."nomarchy/current/theme/apps/hyprland.conf" = lib.mkIf (!hasHyprlandConf) {
source = nordAppsPath + "/hyprland.conf";
};
# Legacy compatibility: symlink apps/hyprland.conf to root for scripts expecting old path
xdg.configFile."nomarchy/current/theme/hyprland.conf" = {
source =
if hasHyprlandConf
then themeAppsPath + "/hyprland.conf"
else nordAppsPath + "/hyprland.conf";
};
xdg.configFile."nomarchy/current/theme.name".text = config.nomarchy.theme;

View File

@@ -0,0 +1,128 @@
{ config, lib, pkgs, ... }:
# Theme Loader Module
#
# This module handles loading and deploying theme-specific application configs.
# It reads the active theme from state and deploys configs from the theme's apps/
# subdirectory to appropriate locations.
#
# Theme structure expected:
# assets/themes/<theme-name>/
# ├── colors.toml # Color palette (required)
# ├── light.mode # Marker for light themes (optional)
# ├── icons.theme # Icon theme name
# ├── backgrounds/ # Wallpapers
# ├── apps/ # App-specific themed configs
# │ ├── alacritty.toml
# │ ├── kitty.conf
# │ ├── waybar.css
# │ ├── hyprland-colors.conf
# │ ├── mako.conf
# │ ├── swayosd.css
# │ ├── btop.theme
# │ ├── vscode.json
# │ └── neovim.lua
# └── preview.png
let
nomarchyLib = import ../lib { inherit lib; };
assetsPath = ../../assets/themes;
activeTheme = config.nomarchy.theme;
themePath = assetsPath + "/${activeTheme}";
themeAppsPath = themePath + "/apps";
# Check if a theme has an apps directory
themeHasApps = builtins.pathExists themeAppsPath;
# Get the color palette for template processing
palette = nomarchyLib.getPalette activeTheme;
# Helper to check if a file exists in theme
hasThemeFile = name: builtins.pathExists (themePath + "/${name}");
hasThemeAppFile = name: themeHasApps && builtins.pathExists (themeAppsPath + "/${name}");
# All app configs are now in apps/ subdirectory
# Legacy root-level files are no longer supported
in
{
options.nomarchy.themeLoader = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to enable automatic theme app config loading.";
};
apps = {
btop = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load btop theme from active theme.";
};
waybar = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load waybar CSS from active theme.";
};
mako = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load mako config from active theme.";
};
kitty = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load kitty config from active theme.";
};
alacritty = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load alacritty config from active theme.";
};
};
};
config = lib.mkIf config.nomarchy.themeLoader.enable {
# Deploy btop theme if available in apps/
xdg.configFile."btop/themes/nomarchy.theme" = lib.mkIf (config.nomarchy.themeLoader.apps.btop && hasThemeAppFile "btop.theme") {
source = lib.mkDefault (themeAppsPath + "/btop.theme");
};
# Note: Waybar CSS is generated inline in waybar.nix using colorScheme
# This loader would be used if themes provide complete waybar.css overrides
# For now, theme waybar.css files are only used for themes that need
# significantly different styling (like retro-82)
# Create theme info file for scripts
xdg.configFile."nomarchy/theme-loader/current".text = ''
THEME_NAME="${activeTheme}"
THEME_PATH="${toString themePath}"
THEME_HAS_APPS="${if themeHasApps then "true" else "false"}"
IS_LIGHT_MODE="${if config.nomarchy.isLightMode then "true" else "false"}"
ICONS_THEME="${config.nomarchy.iconsTheme}"
'';
# Expose palette as shell-sourceable file for scripts
xdg.configFile."nomarchy/theme-loader/palette.sh".text = ''
# Auto-generated palette for ${activeTheme}
# Source this file in scripts to get theme colors
BASE00="${palette.base00}"
BASE01="${palette.base01}"
BASE02="${palette.base02}"
BASE03="${palette.base03}"
BASE04="${palette.base04}"
BASE05="${palette.base05}"
BASE06="${palette.base06}"
BASE07="${palette.base07}"
BASE08="${palette.base08}"
BASE09="${palette.base09}"
BASE0A="${palette.base0A}"
BASE0B="${palette.base0B}"
BASE0C="${palette.base0C}"
BASE0D="${palette.base0D}"
BASE0E="${palette.base0E}"
BASE0F="${palette.base0F}"
'';
};
}

View File

@@ -1,42 +1,43 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:
let
palettes = import ../../assets/themes/nomarchy-palettes.nix;
themeNames = builtins.attrNames palettes;
themeList = builtins.concatStringsSep "\\n" themeNames;
nomarchyLib = import ../lib { inherit lib; };
themeList = builtins.concatStringsSep "\\n" nomarchyLib.themeNames;
nomarchy-theme-selector = pkgs.writeShellScriptBin "nomarchy-theme-selector" ''
SELECTED_THEME=$(echo -e "${themeList}" | walker --dmenu)
if [ -n "$SELECTED_THEME" ]; then
nomarchy-theme-set "$SELECTED_THEME"
fi
'';
nomarchy-font-selector = pkgs.writeShellScriptBin "nomarchy-font-selector" ''
STATE_DIR="${config.home.homeDirectory}/.config/home-manager"
STATE_DIR="${config.home.homeDirectory}/.config/nomarchy"
STATE_FILE="$STATE_DIR/state.json"
mkdir -p "$STATE_DIR"
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
# Simple list of common nerd fonts, could be expanded
FONTS="JetBrainsMono Nerd Font\nRobotoMono Nerd Font\nFiraCode Nerd Font\nUbuntuMono Nerd Font"
SELECTED_FONT=$(echo -e "$FONTS" | walker --dmenu)
if [ -n "$SELECTED_FONT" ]; then
TMP_JSON=$(mktemp)
jq ".font = \"$SELECTED_FONT\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
jq --arg font "$SELECTED_FONT" '.font = $font' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
env-update
fi
'';
nomarchy-wallpaper-selector = pkgs.writeShellScriptBin "nomarchy-wallpaper-selector" ''
STATE_DIR="${config.home.homeDirectory}/.config/home-manager"
STATE_DIR="${config.home.homeDirectory}/.config/nomarchy"
STATE_FILE="$STATE_DIR/state.json"
THEMES_DIR="${config.xdg.dataHome}/nomarchy/themes"
mkdir -p "$STATE_DIR"
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
# List all images in all themes backgrounds
# We search in the system-wide flake source for distro wallpapers to avoid Nix Store bloat
WALLPAPERS=$(find "${config.xdg.dataHome}/nomarchy/themes" -type f \( -name "*.jpg" -o -name "*.png" \) 2>/dev/null)
@@ -49,10 +50,10 @@ let
WALLPAPERS="$WALLPAPERS\n$(find "${config.home.homeDirectory}/.config/nomarchy/themes" -type f \( -name "*.jpg" -o -name "*.png" \))"
fi
SELECTED_WP=$(echo -e "$WALLPAPERS" | walker --dmenu)
if [ -n "$SELECTED_WP" ]; then
TMP_JSON=$(mktemp)
jq ".wallpaper = \"$SELECTED_WP\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
jq --arg wp "$SELECTED_WP" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
swww img "$SELECTED_WP" --transition-type outer --transition-pos 0.85,0.97 --transition-step 90 &
env-update
fi

View File

@@ -1,17 +1,53 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:
let
themeConfig = builtins.fromJSON (builtins.readFile (../../assets/themes + "/${config.nomarchy.theme}/vscode.json"));
# Development extensions that match the system theme
devExtensions = with pkgs.vscode-extensions; [
# Language support
ms-python.python
rust-lang.rust-analyzer
golang.go
jnoortheen.nix-ide
# Git integration
eamodio.gitlens
# Editor enhancements
esbenp.prettier-vscode
dbaeumer.vscode-eslint
bradlc.vscode-tailwindcss
# Theme extensions (provide color themes matching nomarchy palettes)
catppuccin.catppuccin-vsc
enkia.tokyo-night
arcticicestudio.nord-visual-studio-code
sainnhe.everforest
mvllow.rose-pine
];
in
{
programs.vscode = {
enable = true;
package = pkgs.vscode;
userSettings = {
"update.mode" = "none";
"workbench.colorTheme" = themeConfig.name;
"window.titleBarStyle" = "custom";
options.nomarchy.vscode = {
devExtensions = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to install development extensions for VSCode.";
};
};
config = {
programs.vscode = {
enable = lib.mkDefault true;
package = lib.mkDefault pkgs.vscode;
userSettings = lib.mkDefault {
"update.mode" = "none";
"workbench.colorTheme" = themeConfig.name;
"window.titleBarStyle" = "custom";
"editor.fontFamily" = "'${config.nomarchy.fonts.monospace}', 'Droid Sans Mono', monospace";
"terminal.integrated.fontFamily" = config.nomarchy.fonts.monospace;
};
extensions = lib.mkIf config.nomarchy.vscode.devExtensions (lib.mkDefault devExtensions);
};
# extensions = with pkgs.vscode-extensions; [ ... ];
};
}

View File

@@ -1,10 +1,10 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:
{
programs.walker = {
enable = true;
runAsService = true;
config = {
enable = lib.mkDefault true;
runAsService = lib.mkDefault true;
config = lib.mkDefault {
theme = "nomarchy";
ui = {
anchors = {
@@ -14,7 +14,7 @@
selection_wrap = true;
hide_action_hints = true;
placeholders = {
"default" = { input = " Search..."; list = "No Results"; };
"default" = { input = " Search..."; list = "No Results"; };
};
keybinds = {
quick_activate = [];
@@ -38,7 +38,7 @@
];
};
};
themes."nomarchy" = {
themes."nomarchy" = lib.mkDefault {
style = ''
* {
color: #${config.colorScheme.palette.base05};

View File

@@ -1,10 +1,10 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:
{
programs.waybar = {
enable = true;
systemd.enable = true;
style = ''
enable = lib.mkDefault true;
systemd.enable = lib.mkDefault true;
style = lib.mkDefault ''
* {
background-color: #${config.colorScheme.palette.base00};
color: #${config.colorScheme.palette.base05};
@@ -25,7 +25,7 @@
min-width: 9px;
color: #${config.colorScheme.palette.base04};
}
#workspaces button.focused, #workspaces button.active {
color: #${config.colorScheme.palette.base0B};
}
@@ -40,10 +40,10 @@
#tray { margin-right: 16px; }
#bluetooth { margin-right: 17px; }
'';
settings = [ (builtins.fromJSON (builtins.readFile ../../config/waybar/config.jsonc)) ];
settings = lib.mkDefault [ (builtins.fromJSON (builtins.readFile ../../config/waybar/config.jsonc)) ];
};
home.packages = with pkgs; [
home.packages = lib.mkDefault (with pkgs; [
font-awesome
];
]);
}