{ config, lib, pkgs, ... }: let nomarchyLib = import ../../lib { inherit lib; }; # Single source of truth for default values when state.json is missing # a key. Both core/system/options.nix and core/home/options.nix read # from this same file — changing a default in one place updates # everywhere. (Was: each consumer hardcoded its own `or X` literal, # which is how the summer-night/nord split lived for so long.) schema = import ../../lib/state-schema.nix { inherit lib; }; assetsPath = ../../themes/palettes; # Read unified state from ~/.config/nomarchy/state.json togglesState = nomarchyLib.readHomeState config.home.homeDirectory; cfg = config.nomarchy; # The resolved values, in state.json's on-disk shape. Seeded into the file # so runtime consumers (nomarchy-installed-summary, the setters) see the # real defaults instead of a missing key — without this the summary shows # "—" for theme/font/panel on any system the installer didn't write to. seedJSON = builtins.toJSON { inherit (cfg) theme wallpaper panelPosition nightlightTemperature; font = cfg.fonts.monospace; inherit (cfg.toggles) suspend screensaver idle nightlight waybar; inherit (cfg.hyprland) gaps_in gaps_out border_size; }; stateFile = "${config.home.homeDirectory}/.config/nomarchy/state.json"; in { # Every assignment uses lib.mkDefault so a downstream /etc/nixos/home.nix # can override the state.json-derived value. Without mkDefault, every # option here would resolve at default priority and conflict on # assignment from the user's config. config = { nomarchy = { toggles = { suspend = lib.mkDefault (togglesState.suspend or schema.home.suspend); screensaver = lib.mkDefault (togglesState.screensaver or schema.home.screensaver); idle = lib.mkDefault (togglesState.idle or schema.home.idle); nightlight = lib.mkDefault (togglesState.nightlight or schema.home.nightlight); waybar = lib.mkDefault (togglesState.waybar or schema.home.waybar); }; nightlightTemperature = lib.mkDefault (togglesState.nightlightTemperature or schema.home.nightlightTemperature); theme = lib.mkDefault (togglesState.theme or schema.home.theme); wallpaper = lib.mkDefault (togglesState.wallpaper or schema.home.wallpaper); panelPosition = lib.mkDefault (togglesState.panelPosition or schema.home.panelPosition); hyprland = { gaps_in = lib.mkDefault (togglesState.hyprland.gaps_in or schema.home.hyprland.gaps_in); gaps_out = lib.mkDefault (togglesState.hyprland.gaps_out or schema.home.hyprland.gaps_out); border_size = lib.mkDefault (togglesState.hyprland.border_size or schema.home.hyprland.border_size); }; fonts.monospace = lib.mkDefault (togglesState.font or schema.home.font); # Derived properties from the theme directory isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode { themeName = togglesState.theme or schema.home.theme; inherit assetsPath; }); iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme { themeName = togglesState.theme or schema.home.theme; inherit assetsPath; }); }; # Backfill any state.json key the user hasn't set. Existing values always # win (`defaults * existing`), so a user's theme/font/etc. and any # runtime-only keys (welcome_done, …) are never clobbered — this only # fills the gaps so the file reflects the system's actual defaults. home.activation.seedNomarchyState = lib.hm.dag.entryAfter [ "writeBoundary" ] '' run mkdir -p "$(dirname ${lib.escapeShellArg stateFile})" tmp="$(${pkgs.coreutils}/bin/mktemp)" # Build the next file contents in $tmp first; only the final move is # guarded by `run` so a dry-run never mutates the real state file. if [ -e ${lib.escapeShellArg stateFile} ]; then ${pkgs.jq}/bin/jq -n --argjson d ${lib.escapeShellArg seedJSON} \ --slurpfile s ${lib.escapeShellArg stateFile} '$d * $s[0]' > "$tmp" else printf '%s\n' ${lib.escapeShellArg seedJSON} > "$tmp" fi run mv "$tmp" ${lib.escapeShellArg stateFile} ''; }; }