feat: Nomarchy ground-up rewrite on NixOS 26.05
Full replacement of the previous iteration, rebuilt around three ideas:
- Pure evaluation: theme-state.json lives inside the flake and is read
via the nomarchy.stateFile option — no --impure, ever.
- All-Home-Manager theming: `nomarchy-theme-sync apply` writes the JSON
and runs `home-manager switch`; every theme change is one atomic,
rollbackable generation. Wallpaper (swww) is the sole runtime piece.
- Flat, downstream-first layout: modules/{nixos,home} with one
options.nix each, exported as nixosModules/homeModules + overlay +
flake template; system (nixos-rebuild) and desktop (home-manager
switch) rebuild paths are fully split.
Ships 21 themes imported from the previous iteration (palettes,
wallpapers, btop themes, six whole-swap Waybar identities), Stylix for
the GTK/Qt/cursor long tail, a live ISO target with offline theme
switching, and docs/TESTING.md with the QEMU verification workflow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
155
modules/home/waybar.nix
Normal file
155
modules/home/waybar.nix
Normal file
@@ -0,0 +1,155 @@
|
||||
# Waybar — two-tier theming:
|
||||
#
|
||||
# 1. Default: structure, fonts, geometry AND palette baked from
|
||||
# theme-state.json (colors as GTK named colors, @define-color).
|
||||
#
|
||||
# 2. Whole-swap: themes with their own visual identity ship
|
||||
# <themesDir>/<slug>/waybar.css (and optionally waybar.jsonc) which
|
||||
# replace the generated style/layout entirely. Probed at eval time —
|
||||
# pure, it's flake source.
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
t = config.nomarchy.theme;
|
||||
|
||||
# Per-theme override probe.
|
||||
assetDir = config.nomarchy.themesDir + "/${t.slug}";
|
||||
styleOverride = assetDir + "/waybar.css";
|
||||
configOverride = assetDir + "/waybar.jsonc";
|
||||
hasStyleOverride = builtins.pathExists styleOverride;
|
||||
hasConfigOverride = builtins.pathExists configOverride;
|
||||
|
||||
# The palette as GTK named colors, straight from the JSON.
|
||||
colorDefs = lib.concatStringsSep "\n"
|
||||
(lib.mapAttrsToList (name: value: "@define-color ${name} ${value};") t.colors);
|
||||
|
||||
generatedSettings = {
|
||||
layer = "top";
|
||||
position = "top";
|
||||
height = 34;
|
||||
margin-top = t.ui.gapsOut;
|
||||
margin-left = t.ui.gapsOut;
|
||||
margin-right = t.ui.gapsOut;
|
||||
spacing = 8;
|
||||
|
||||
# waybar re-reads style.css when it changes on disk, so a
|
||||
# home-manager switch restyles the running bar without a restart.
|
||||
reload_style_on_change = true;
|
||||
|
||||
modules-left = [ "hyprland/workspaces" "hyprland/window" ];
|
||||
modules-center = [ "clock" ];
|
||||
modules-right = [ "tray" "pulseaudio" "network" "cpu" "memory" "battery" ];
|
||||
|
||||
"hyprland/workspaces" = {
|
||||
format = "{icon}";
|
||||
on-click = "activate";
|
||||
};
|
||||
|
||||
"hyprland/window" = {
|
||||
max-length = 48;
|
||||
separate-outputs = true;
|
||||
};
|
||||
|
||||
clock = {
|
||||
format = "{:%H:%M}";
|
||||
format-alt = "{:%A %d %B %Y}";
|
||||
tooltip-format = "<tt><small>{calendar}</small></tt>";
|
||||
};
|
||||
|
||||
pulseaudio = {
|
||||
format = "{icon} {volume}%";
|
||||
format-muted = "";
|
||||
format-icons.default = [ "" "" "" ];
|
||||
on-click = "pamixer -t";
|
||||
};
|
||||
|
||||
network = {
|
||||
format-wifi = " {essid}";
|
||||
format-ethernet = "";
|
||||
format-disconnected = "";
|
||||
tooltip-format = "{ipaddr} via {gwaddr}";
|
||||
};
|
||||
|
||||
cpu.format = " {usage}%";
|
||||
memory.format = " {percentage}%";
|
||||
|
||||
battery = {
|
||||
states = { warning = 25; critical = 10; };
|
||||
format = "{icon} {capacity}%";
|
||||
format-charging = " {capacity}%";
|
||||
format-icons = [ "" "" "" "" "" ];
|
||||
};
|
||||
|
||||
tray.spacing = 8;
|
||||
};
|
||||
|
||||
generatedStyle = ''
|
||||
/* Palette baked from theme-state.json */
|
||||
${colorDefs}
|
||||
|
||||
* {
|
||||
font-family: "${t.fonts.ui}", "${t.fonts.mono}";
|
||||
font-size: ${toString t.fonts.size}pt;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
window#waybar {
|
||||
background: alpha(@base, 0.85);
|
||||
color: @text;
|
||||
border: ${toString t.ui.borderSize}px solid alpha(@accent, 0.4);
|
||||
border-radius: ${toString t.ui.rounding}px;
|
||||
}
|
||||
|
||||
#workspaces button {
|
||||
padding: 0 8px;
|
||||
color: @muted;
|
||||
border-radius: ${toString t.ui.rounding}px;
|
||||
}
|
||||
|
||||
#workspaces button.active {
|
||||
color: @base;
|
||||
background: @accent;
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
color: @base;
|
||||
background: @bad;
|
||||
}
|
||||
|
||||
#window {
|
||||
color: @subtext;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
#clock {
|
||||
color: @text;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#tray, #pulseaudio, #network, #cpu, #memory, #battery {
|
||||
color: @subtext;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
#pulseaudio.muted { color: @muted; }
|
||||
#battery.warning { color: @warn; }
|
||||
#battery.critical { color: @bad; }
|
||||
#battery.charging { color: @good; }
|
||||
'';
|
||||
in
|
||||
{
|
||||
programs.waybar = lib.mkIf config.nomarchy.waybar.enable {
|
||||
enable = true;
|
||||
systemd.enable = true; # started/stopped with graphical-session.target
|
||||
|
||||
settings.mainBar =
|
||||
if hasConfigOverride
|
||||
then builtins.fromJSON (builtins.readFile configOverride)
|
||||
else generatedSettings;
|
||||
|
||||
style =
|
||||
if hasStyleOverride
|
||||
then builtins.readFile styleOverride
|
||||
else generatedStyle;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user