refactor(#107): theme-state.json → state.json, theme-sync → state-sync
Some checks failed
Check / eval (push) Has been cancelled
Some checks failed
Check / eval (push) Has been cancelled
The machine flake's git-tracked settings file is system state, not "theme" only — rename it to state.json. CLI becomes nomarchy-state-sync with a nomarchy-theme-sync symlink for scripts and muscle memory. Eval (mkFlake, doctor, lifecycle) still accepts theme-state.json; the next write migrates to state.json and removes the legacy file. Documented in MIGRATION.md; drop the CLI alias after release notes.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Auto time-of-day theme switch (BACKLOG #79, VISION § D). A user timer
|
||||
# periodically runs `nomarchy-theme-sync auto`, which reads
|
||||
# periodically runs `nomarchy-state-sync auto`, which reads
|
||||
# settings.autoTheme = { enable, day, night, sunrise, sunset } from the
|
||||
# state file and applies the day or night preset for the current clock —
|
||||
# through the SAME one engine as a manual `apply` (no second pipeline).
|
||||
@@ -20,7 +20,7 @@ lib.mkIf (config.nomarchy.settings.autoTheme.enable or false) {
|
||||
# The rebuild (`home-manager switch`) and its tools resolve from the
|
||||
# system + per-user profiles — same PATH shape nomarchy-updates uses.
|
||||
Environment = "PATH=/run/current-system/sw/bin:/etc/profiles/per-user/${config.home.username}/bin";
|
||||
ExecStart = "${pkgs.nomarchy-theme-sync}/bin/nomarchy-theme-sync auto";
|
||||
ExecStart = "${pkgs.nomarchy-state-sync}/bin/nomarchy-state-sync auto";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{
|
||||
imports = [
|
||||
./options.nix # the nomarchy.* option surface
|
||||
./theme.nix # ingests theme-state.json, owns the live-sync hooks
|
||||
./theme.nix # ingests state.json, owns the live-sync hooks
|
||||
./stylix.nix # GTK/Qt/cursor/fonts from the same JSON
|
||||
./hyprland.nix
|
||||
./waybar.nix
|
||||
@@ -106,8 +106,8 @@
|
||||
# (system nixpkgs.config alone does not cover those entry points).
|
||||
NIXPKGS_ALLOW_UNFREE = "1";
|
||||
|
||||
# Where the Nomarchy flake (and therefore theme-state.json) lives on
|
||||
# disk. nomarchy-theme-sync writes its state here; rebuilds read from
|
||||
# Where the Nomarchy flake (and therefore state.json) lives on
|
||||
# disk. nomarchy-state-sync writes its state here; rebuilds read from
|
||||
# here. Clone/symlink your flake to this path.
|
||||
NOMARCHY_PATH = "$HOME/.nomarchy";
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
Environment = [
|
||||
"PATH=${lib.makeBinPath [
|
||||
pkgs.libnotify
|
||||
pkgs.nomarchy-theme-sync
|
||||
pkgs.nomarchy-state-sync
|
||||
pkgs.coreutils
|
||||
]}"
|
||||
"NOMARCHY_PATH=%h/.nomarchy"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Hyprland — fully driven by config.nomarchy.theme (theme-state.json).
|
||||
# Hyprland — fully driven by config.nomarchy.theme (state.json).
|
||||
# Gaps, borders and colors are baked from the JSON at eval time; theme
|
||||
# changes arrive via `home-manager switch`.
|
||||
{ config, lib, pkgs, ... }:
|
||||
@@ -8,7 +8,7 @@ let
|
||||
c = t.colors;
|
||||
inherit (config.nomarchy.lib) rgb rgba;
|
||||
|
||||
# nomarchy-theme-sync — the in-flake state writer the keyboard watcher uses
|
||||
# nomarchy-state-sync — the in-flake state writer the keyboard watcher uses
|
||||
# to remember per-device layouts (same path night-light's toggle takes).
|
||||
sync = lib.getExe config.nomarchy.package;
|
||||
|
||||
@@ -883,7 +883,7 @@ in
|
||||
"awww-daemon"
|
||||
# Paint the wallpaper as soon as the session is up (waits for
|
||||
# the daemon internally).
|
||||
"nomarchy-theme-sync wallpaper"
|
||||
"nomarchy-state-sync wallpaper"
|
||||
# Polkit authentication agent — without one, EVERY pkexec/polkit
|
||||
# prompt in the session fails silently (btrfs-assistant-launcher
|
||||
# was the discovery case). hyprpolkitagent is Hyprland's own Qt
|
||||
@@ -903,8 +903,8 @@ in
|
||||
];
|
||||
|
||||
# ── Theme-driven look ──────────────────────────────────────────
|
||||
# These flow from theme-state.json and stay at NORMAL priority:
|
||||
# change them with `nomarchy-theme-sync set ui.<key>` (the intended
|
||||
# These flow from state.json and stay at NORMAL priority:
|
||||
# change them with `nomarchy-state-sync set ui.<key>` (the intended
|
||||
# path), or lib.mkForce in home.nix to hardcode against the theme.
|
||||
# The non-theme knobs around them are lib.mkDefault — override
|
||||
# those with a plain home.nix assignment. See docs/OVERRIDES.md.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# hyprlock + hypridle — screen locking and idle management, themed from
|
||||
# theme-state.json. One concern, one file: hypridle drives WHEN (idle
|
||||
# state.json. One concern, one file: hypridle drives WHEN (idle
|
||||
# lock, display off, suspend, lock-before-sleep), hyprlock is the
|
||||
# themed lock screen itself (also behind the power menu's Lock entry).
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
# home-manager switch (progress via notify-send).
|
||||
{ mods = "$mod"; key = "T"; action = "exec, nomarchy-menu theme"; desc = "Theme picker"; group = "Menu"; }
|
||||
# Cycle the current theme's wallpapers (instant, no rebuild).
|
||||
{ mods = "$mod SHIFT"; key = "T"; action = "exec, nomarchy-theme-sync bg next"; desc = "Next wallpaper"; group = "Menu"; }
|
||||
{ mods = "$mod SHIFT"; key = "T"; action = "exec, nomarchy-state-sync bg next"; desc = "Next wallpaper"; group = "Menu"; }
|
||||
|
||||
# Power menu via the dispatcher. Not Escape: Super+Escape gets
|
||||
# swallowed before reaching the dispatcher on some setups.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Kitty — Nomarchy's only terminal, themed from theme-state.json.
|
||||
# Kitty — Nomarchy's only terminal, themed from state.json.
|
||||
# Colors, fonts and the full 16-color ANSI palette are baked from the
|
||||
# JSON at eval time (same contract Ghostty used to have). Always
|
||||
# installed: SUPER+Return, SUPER+E, doctor, calendar, and $TERMINAL
|
||||
|
||||
@@ -88,11 +88,11 @@ in
|
||||
# ── Required ───────────────────────────────────────────────────
|
||||
stateFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
example = lib.literalExpression "./theme-state.json";
|
||||
example = lib.literalExpression "./state.json";
|
||||
description = ''
|
||||
Path to theme-state.json, the single source of truth for all UI
|
||||
Path to state.json, the single source of truth for all UI
|
||||
configuration. Must live inside your flake (so evaluation stays
|
||||
pure) and be git-tracked. nomarchy-theme-sync writes to the
|
||||
pure) and be git-tracked. nomarchy-state-sync writes to the
|
||||
on-disk copy; rebuilds bake it into the generation.
|
||||
'';
|
||||
};
|
||||
@@ -182,9 +182,9 @@ in
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.nomarchy-theme-sync;
|
||||
defaultText = lib.literalExpression "pkgs.nomarchy-theme-sync";
|
||||
description = "The nomarchy-theme-sync package (provided by overlays.default).";
|
||||
default = pkgs.nomarchy-state-sync;
|
||||
defaultText = lib.literalExpression "pkgs.nomarchy-state-sync";
|
||||
description = "The nomarchy-state-sync package (provided by overlays.default).";
|
||||
};
|
||||
|
||||
themesDir = lib.mkOption {
|
||||
@@ -379,7 +379,7 @@ in
|
||||
type = lib.types.bool;
|
||||
default = config.nomarchy.settings.fingerprint.pam or false;
|
||||
defaultText = lib.literalExpression
|
||||
"(settings.fingerprint.pam from theme-state.json) or false";
|
||||
"(settings.fingerprint.pam from state.json) or false";
|
||||
description = ''
|
||||
Unlock the lock screen with a fingerprint as well as the password, and
|
||||
say so on the input field.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# lock). The media keys drive it via swayosd-client (it performs the
|
||||
# action AND shows the OSD), so changing volume or brightness gives the
|
||||
# visual feedback that bare wpctl/brightnessctl didn't. Themed from
|
||||
# theme-state.json. Brightness needs the backlight udev rule shipped
|
||||
# state.json. Brightness needs the backlight udev rule shipped
|
||||
# system-side (modules/nixos/default.nix → services.udev.packages).
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# rofi (2.0, native Wayland on 26.05) — launcher + dmenu renderer for
|
||||
# the menu system, themed from theme-state.json. rofi's .rasi is far more
|
||||
# the menu system, themed from state.json. rofi's .rasi is far more
|
||||
# expressive than a flat scheme, so the generated theme styles each
|
||||
# element (accent border, highlighted selection, rounded inputbar); a
|
||||
# themes/<slug>/rofi.rasi whole-swap replaces it entirely.
|
||||
@@ -71,7 +71,7 @@ let
|
||||
|
||||
# One grid row per theme (square thumb + name, ✓ on the active one), and a
|
||||
# Name→slug map so the picked label resolves back to the preset that
|
||||
# nomarchy-theme-sync applies (pretty names ≠ slugs, hence the map). Themes
|
||||
# nomarchy-state-sync applies (pretty names ≠ slugs, hence the map). Themes
|
||||
# with no preview degrade to a plain-name row.
|
||||
themeRows = lib.concatMapStringsSep "\n" (th:
|
||||
let label = th.name + lib.optionalString (th.slug == t.slug) " ✓";
|
||||
@@ -190,7 +190,7 @@ let
|
||||
nomarchy-menu = pkgs.writeShellScriptBin "nomarchy-menu" ''
|
||||
# Nomarchy menu dispatcher — thin presentation layer over
|
||||
# `rofi -dmenu` (via rofi_menu helper); actions delegate to
|
||||
# nomarchy-theme-sync, systemctl, hyprctl and friends. Menu entries
|
||||
# nomarchy-state-sync, systemctl, hyprctl and friends. Menu entries
|
||||
# carry real icons from the theme's icon set (Papirus) via rofi's
|
||||
# per-row icon protocol — see row() below. `nomarchy-menu` with no
|
||||
# argument shows the module picker.
|
||||
@@ -350,7 +350,7 @@ let
|
||||
# power-profile icons (the two granular System ▸ Power profile /
|
||||
# Battery limit rows stay for direct access). No new backend: profile
|
||||
# rows drive powerprofilesctl like the power-profile picker, charge
|
||||
# rows drive the same theme-sync writer as batterylimit. Each half
|
||||
# rows drive the same state-sync writer as batterylimit. Each half
|
||||
# self-gates on its hardware, so a desktop with neither just toasts.
|
||||
haveppd=false; command -v powerprofilesctl >/dev/null 2>&1 && haveppd=true
|
||||
havecharge=false; has_charge_threshold && havecharge=true
|
||||
@@ -359,7 +359,7 @@ let
|
||||
exit 0
|
||||
fi
|
||||
curp=""; $haveppd && curp=$(powerprofilesctl get 2>/dev/null)
|
||||
curc=$(nomarchy-theme-sync get settings.power.batteryChargeLimit 2>/dev/null)
|
||||
curc=$(nomarchy-state-sync get settings.power.batteryChargeLimit 2>/dev/null)
|
||||
choice=$( {
|
||||
if $haveppd; then
|
||||
powerprofilesctl list 2>/dev/null | sed -nE 's/^[* ] ([a-z-]+):$/\1/p' \
|
||||
@@ -384,7 +384,7 @@ let
|
||||
# allows users to restart it) so a non-writable sysfs node still
|
||||
# updates. Fallback: direct echo if the node is group-writable.
|
||||
set_limit() {
|
||||
nomarchy-theme-sync --quiet set settings.power.batteryChargeLimit "$1" --no-switch
|
||||
nomarchy-state-sync --quiet set settings.power.batteryChargeLimit "$1" --no-switch
|
||||
n="$1"; [ "$n" = null ] && n=100
|
||||
# Root oneshot sets Custom charge type (Dell) + thresholds.
|
||||
applied=
|
||||
@@ -452,7 +452,7 @@ ${themeRows}
|
||||
[ "$choice" = "$BACK" ] && exec "$0"
|
||||
choice="''${choice% ✓}" # drop the active marker if present
|
||||
slug="''${THEME_SLUG[$choice]:-}"
|
||||
[ -n "$slug" ] && exec nomarchy-theme-sync apply "$slug" ;;
|
||||
[ -n "$slug" ] && exec nomarchy-state-sync apply "$slug" ;;
|
||||
|
||||
clipboard)
|
||||
sel=$( { cliphist list; printf '%s\n' "$BACK"; } | rofi_menu -p clip) || exit 0
|
||||
@@ -691,7 +691,7 @@ ${themeRows}
|
||||
pos=$(printf '%s' "$mons" | jq -r --arg n "$name" '.[]|select(.name==$n)|"\(.x)x\(.y)"')
|
||||
scale=$(printf '%s' "$mons" | jq -r --arg n "$name" '.[]|select(.name==$n)|.scale')
|
||||
if hyprctl keyword monitor "$name,$mode,$pos,$scale" >/dev/null 2>&1; then
|
||||
nomarchy-theme-sync --quiet set "settings.monitors.$name" "$mode" --no-switch
|
||||
nomarchy-state-sync --quiet set "settings.monitors.$name" "$mode" --no-switch
|
||||
got=$(hyprctl monitors -j | jq -r --arg n "$name" \
|
||||
'.[]|select(.name==$n)|"\(.width)x\(.height)@\(.refreshRate|round)"')
|
||||
notify-send "Display" "$name → $mode (now ${got:-?})"
|
||||
@@ -759,7 +759,7 @@ ${themeRows}
|
||||
# Power / Keybindings). SUPER+T / SUPER+SHIFT+T still hit leaves.
|
||||
choice=$( {
|
||||
row "Theme${menuHint "theme"}" preferences-desktop-theme
|
||||
row "Next wallpaper${hintForAction "exec, nomarchy-theme-sync bg next"}" preferences-desktop-wallpaper
|
||||
row "Next wallpaper${hintForAction "exec, nomarchy-state-sync bg next"}" preferences-desktop-wallpaper
|
||||
row "Reset wallpaper (auto)" preferences-desktop-wallpaper
|
||||
# Use the toggle script (knows hyprsunset vs wlsunset); a hard-coded
|
||||
# hyprsunset-only check always showed (off) in geo mode and when
|
||||
@@ -768,7 +768,7 @@ ${themeRows}
|
||||
then row "Night light (on)" weather-clear-night
|
||||
else row "Night light (off)" weather-clear-night
|
||||
fi
|
||||
if [ "$(nomarchy-theme-sync get settings.autoTheme.enable 2>/dev/null)" = true ]
|
||||
if [ "$(nomarchy-state-sync get settings.autoTheme.enable 2>/dev/null)" = true ]
|
||||
then row "Auto theme (on)" preferences-desktop-theme
|
||||
else row "Auto theme (off)" preferences-desktop-theme
|
||||
fi
|
||||
@@ -778,8 +778,8 @@ ${themeRows}
|
||||
"$BACK") exec "$0" ;;
|
||||
*"Auto theme"*) exec "$0" autotheme ;;
|
||||
*Theme*) exec "$0" theme ;;
|
||||
*"Next wallpaper"*) exec nomarchy-theme-sync bg next ;;
|
||||
*"Reset wallpaper"*) exec nomarchy-theme-sync bg auto ;;
|
||||
*"Next wallpaper"*) exec nomarchy-state-sync bg next ;;
|
||||
*"Reset wallpaper"*) exec nomarchy-state-sync bg auto ;;
|
||||
*"Night light"*) exec "$0" nightlight ;;
|
||||
esac ;;
|
||||
|
||||
@@ -796,7 +796,7 @@ ${themeRows}
|
||||
if [ "$p" = "$active" ]; then printf '● %s\n' "$p"; else printf '○ %s\n' "$p"; fi
|
||||
done
|
||||
printf 'Base layout\n'
|
||||
if [ "$(nomarchy-theme-sync get settings.displayProfileAuto 2>/dev/null)" = true ]
|
||||
if [ "$(nomarchy-state-sync get settings.displayProfileAuto 2>/dev/null)" = true ]
|
||||
then printf 'Auto-switch (on)\n'
|
||||
else printf 'Auto-switch (off)\n'
|
||||
fi
|
||||
@@ -808,11 +808,11 @@ ${themeRows}
|
||||
*"Auto-switch"*)
|
||||
# Instant in-flake flag; the watcher reads it live on the next
|
||||
# output change — no rebuild, no unit juggling.
|
||||
if [ "$(nomarchy-theme-sync get settings.displayProfileAuto 2>/dev/null)" = true ]; then
|
||||
nomarchy-theme-sync --quiet set settings.displayProfileAuto false --no-switch
|
||||
if [ "$(nomarchy-state-sync get settings.displayProfileAuto 2>/dev/null)" = true ]; then
|
||||
nomarchy-state-sync --quiet set settings.displayProfileAuto false --no-switch
|
||||
notify-send "Display profiles" "Auto-switch off — profiles change only from this menu."
|
||||
else
|
||||
nomarchy-theme-sync --quiet set settings.displayProfileAuto true --no-switch
|
||||
nomarchy-state-sync --quiet set settings.displayProfileAuto true --no-switch
|
||||
notify-send "Display profiles" "Auto-switch on — plugging/unplugging outputs picks the matching profile."
|
||||
fi
|
||||
exec "$0" display-profile ;;
|
||||
@@ -869,12 +869,12 @@ ${themeRows}
|
||||
# (settings.autoTheme, applied by the nomarchy-auto-theme timer from
|
||||
# autotheme.nix). The enable flag GATES the timer's install, so
|
||||
# turning it *on* must rebuild; day/night/times are read live by
|
||||
# `nomarchy-theme-sync auto`, so those writes are instant
|
||||
# `nomarchy-state-sync auto`, so those writes are instant
|
||||
# (--no-switch). Enabling writes the flag then `auto --force`, so a
|
||||
# single rebuild both installs the timer and applies the right theme
|
||||
# now. Disabling is instant: the flag flips and `auto` self-gates to
|
||||
# a no-op, so the lingering timer does nothing until the next rebuild.
|
||||
at_get() { nomarchy-theme-sync get "settings.autoTheme.$1" 2>/dev/null; }
|
||||
at_get() { nomarchy-state-sync get "settings.autoTheme.$1" 2>/dev/null; }
|
||||
en=$(at_get enable); day=$(at_get day); night=$(at_get night)
|
||||
sunrise=$(at_get sunrise); sunset=$(at_get sunset)
|
||||
choice=$( {
|
||||
@@ -892,25 +892,25 @@ ${themeRows}
|
||||
"$BACK") exec "$0" lookfeel ;;
|
||||
*"Auto theme"*)
|
||||
if [ "$en" = true ]; then
|
||||
nomarchy-theme-sync --quiet set settings.autoTheme.enable false --no-switch
|
||||
nomarchy-state-sync --quiet set settings.autoTheme.enable false --no-switch
|
||||
notify-send "Auto theme" "Off — current theme stays."
|
||||
exec "$0" autotheme
|
||||
fi
|
||||
# Need a day + night pair before enabling; default the summer pair.
|
||||
[ -n "$day" ] || nomarchy-theme-sync --quiet set settings.autoTheme.day summer-day --no-switch
|
||||
[ -n "$night" ] || nomarchy-theme-sync --quiet set settings.autoTheme.night summer-night --no-switch
|
||||
nomarchy-theme-sync --quiet set settings.autoTheme.enable true --no-switch
|
||||
[ -n "$day" ] || nomarchy-state-sync --quiet set settings.autoTheme.day summer-day --no-switch
|
||||
[ -n "$night" ] || nomarchy-state-sync --quiet set settings.autoTheme.night summer-night --no-switch
|
||||
nomarchy-state-sync --quiet set settings.autoTheme.enable true --no-switch
|
||||
notify-send "Auto theme" "On — applying the theme for now…"
|
||||
exec nomarchy-theme-sync auto --force ;; # one rebuild: install timer + apply
|
||||
exec nomarchy-state-sync auto --force ;; # one rebuild: install timer + apply
|
||||
"Day theme"*)
|
||||
slug=$( { nomarchy-theme-sync list; printf '%s\n' "$BACK"; } | rofi_menu -p "Day theme") || exec "$0" autotheme
|
||||
slug=$( { nomarchy-state-sync list; printf '%s\n' "$BACK"; } | rofi_menu -p "Day theme") || exec "$0" autotheme
|
||||
[ "$slug" != "$BACK" ] && [ -n "$slug" ] \
|
||||
&& nomarchy-theme-sync --quiet set settings.autoTheme.day "$slug" --no-switch
|
||||
&& nomarchy-state-sync --quiet set settings.autoTheme.day "$slug" --no-switch
|
||||
exec "$0" autotheme ;;
|
||||
"Night theme"*)
|
||||
slug=$( { nomarchy-theme-sync list; printf '%s\n' "$BACK"; } | rofi_menu -p "Night theme") || exec "$0" autotheme
|
||||
slug=$( { nomarchy-state-sync list; printf '%s\n' "$BACK"; } | rofi_menu -p "Night theme") || exec "$0" autotheme
|
||||
[ "$slug" != "$BACK" ] && [ -n "$slug" ] \
|
||||
&& nomarchy-theme-sync --quiet set settings.autoTheme.night "$slug" --no-switch
|
||||
&& nomarchy-state-sync --quiet set settings.autoTheme.night "$slug" --no-switch
|
||||
exec "$0" autotheme ;;
|
||||
"Sunrise"*|"Sunset"*)
|
||||
key=sunrise; label=Sunrise; cur="''${sunrise:-07:00}"
|
||||
@@ -919,7 +919,7 @@ ${themeRows}
|
||||
case "$t" in
|
||||
""|"$BACK") : ;;
|
||||
[01][0-9]:[0-5][0-9]|2[0-3]:[0-5][0-9])
|
||||
nomarchy-theme-sync --quiet set "settings.autoTheme.$key" "$t" --no-switch ;;
|
||||
nomarchy-state-sync --quiet set "settings.autoTheme.$key" "$t" --no-switch ;;
|
||||
*) notify-send "Auto theme" "Ignored '$t' — use HH:MM (24-hour)." ;;
|
||||
esac
|
||||
exec "$0" autotheme ;;
|
||||
@@ -945,18 +945,18 @@ ${themeRows}
|
||||
|
||||
autocommit)
|
||||
# Toggle opt-in auto-commit: every menu/theme mutation also commits
|
||||
# theme-state.json (that file only) in the downstream flake, so
|
||||
# state.json (that file only) in the downstream flake, so
|
||||
# settings history is `git log`; nomarchy-pull/-rebuild/-home
|
||||
# additionally sweep everything else dirty (hand edits, lock bumps)
|
||||
# into a commit first. Nothing in Nix consumes the flag —
|
||||
# the tool reads the live state on each write — so the toggle is
|
||||
# instant, no rebuild. The off-write commits too (the tool fires
|
||||
# when the flag was on before OR after), keeping history consistent.
|
||||
if [ "$(nomarchy-theme-sync get settings.autoCommit 2>/dev/null)" = true ]; then
|
||||
nomarchy-theme-sync --quiet set settings.autoCommit false --no-switch
|
||||
if [ "$(nomarchy-state-sync get settings.autoCommit 2>/dev/null)" = true ]; then
|
||||
nomarchy-state-sync --quiet set settings.autoCommit false --no-switch
|
||||
notify-send "Auto-commit" "Off — menu changes stay uncommitted in your flake."
|
||||
else
|
||||
nomarchy-theme-sync --quiet set settings.autoCommit true --no-switch
|
||||
nomarchy-state-sync --quiet set settings.autoCommit true --no-switch
|
||||
notify-send "Auto-commit" "On — menu changes commit; rebuilds sweep pending edits in too."
|
||||
fi
|
||||
exit 0 ;;
|
||||
@@ -1017,7 +1017,7 @@ ${themeRows}
|
||||
# CLI (services.fprintd). Enroll is interactive → terminal.
|
||||
command -v fprintd-list >/dev/null 2>&1 \
|
||||
|| { notify-send "Fingerprint" "fprintd not available (nomarchy.hardware.fingerprint.enable?)."; exit 0; }
|
||||
pam=$(nomarchy-theme-sync get settings.fingerprint.pam 2>/dev/null || echo false)
|
||||
pam=$(nomarchy-state-sync get settings.fingerprint.pam 2>/dev/null || echo false)
|
||||
case "$pam" in true|True) pam_label="Fingerprint (on)" ;; *) pam_label="Fingerprint (off)" ;; esac
|
||||
choice=$( {
|
||||
# The switch leads: it is the decision this menu exists for, and
|
||||
@@ -1178,7 +1178,7 @@ ${themeRows}
|
||||
&& row "Keyboard${menuHint "keyboard"}" preferences-desktop-keyboard
|
||||
command -v system-config-printer >/dev/null 2>&1 && row "Printers" printer
|
||||
row "Do Not Disturb${menuHint "dnd"}" notification-disabled
|
||||
if [ "$(nomarchy-theme-sync get settings.autoTimezone 2>/dev/null)" = true ]
|
||||
if [ "$(nomarchy-state-sync get settings.autoTimezone 2>/dev/null)" = true ]
|
||||
then row "Auto timezone (on)" preferences-system-time
|
||||
else row "Auto timezone (off)" preferences-system-time
|
||||
fi
|
||||
@@ -1186,15 +1186,15 @@ ${themeRows}
|
||||
# greeter asks; that's the prompt the Fingerprint switch feeds.
|
||||
if command -v nomarchy-autologin >/dev/null 2>&1; then
|
||||
# Unset (get exits 1, empty) and null both read as off. "None" is
|
||||
# what an older theme-sync printed for null — accepted so a menu
|
||||
# what an older state-sync printed for null — accepted so a menu
|
||||
# from a newer generation can't misreport against an older tool.
|
||||
case "$(nomarchy-theme-sync get settings.greeter.autoLogin 2>/dev/null)" in
|
||||
case "$(nomarchy-state-sync get settings.greeter.autoLogin 2>/dev/null)" in
|
||||
null|""|None) row "Auto-login (off)" system-users ;;
|
||||
*) row "Auto-login (on)" system-users ;;
|
||||
esac
|
||||
fi
|
||||
if [ -e "''${NOMARCHY_PATH:-$HOME/.nomarchy}/.git" ]; then
|
||||
if [ "$(nomarchy-theme-sync get settings.autoCommit 2>/dev/null)" = true ]
|
||||
if [ "$(nomarchy-state-sync get settings.autoCommit 2>/dev/null)" = true ]
|
||||
then row "Auto-commit (on)" git
|
||||
else row "Auto-commit (off)" git
|
||||
fi
|
||||
@@ -1255,7 +1255,7 @@ ${themeRows}
|
||||
|| { notify-send "Battery limit" \
|
||||
"This machine's firmware does not expose a charge-stop control (no charge_control_end_threshold in /sys), so a charge limit cannot be set here. The battery still works normally — this is a hardware capability, not a Nomarchy problem."
|
||||
exit 0; }
|
||||
cur=$(nomarchy-theme-sync get settings.power.batteryChargeLimit 2>/dev/null)
|
||||
cur=$(nomarchy-state-sync get settings.power.batteryChargeLimit 2>/dev/null)
|
||||
sel=$( {
|
||||
row "80% (recommended)" battery-080
|
||||
row "90%" battery-090
|
||||
@@ -1265,7 +1265,7 @@ ${themeRows}
|
||||
back
|
||||
} | rofi_menu -show-icons -p "Battery limit (now: ''${cur:-default})") || exit 0
|
||||
set_limit() {
|
||||
nomarchy-theme-sync --quiet set settings.power.batteryChargeLimit "$1" --no-switch
|
||||
nomarchy-state-sync --quiet set settings.power.batteryChargeLimit "$1" --no-switch
|
||||
n="$1"; [ "$n" = null ] && n=100
|
||||
applied=
|
||||
if systemctl restart nomarchy-battery-charge-limit.service 2>/dev/null; then
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Stylix — themes the long tail of applications (GTK, Qt, cursors,
|
||||
# fonts) from the same theme-state.json that drives the live engine.
|
||||
# fonts) from the same state.json that drives the live engine.
|
||||
#
|
||||
# Division of labour: the hot-reload trio (Hyprland, Waybar, Kitty)
|
||||
# is owned by the Nomarchy engine and updates instantly; everything
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# swaync — notification daemon + control centre, themed from
|
||||
# theme-state.json. Until this shipped, nothing rendered notify-send at
|
||||
# state.json. Until this shipped, nothing rendered notify-send at
|
||||
# all: the theme-switch progress toasts, the CLI's font warnings and the
|
||||
# live ISO's welcome message were all invisible.
|
||||
{ config, lib, ... }:
|
||||
@@ -29,7 +29,7 @@ in
|
||||
};
|
||||
|
||||
style = ''
|
||||
/* Palette baked from theme-state.json. Only roles guaranteed to
|
||||
/* Palette baked from state.json. Only roles guaranteed to
|
||||
contrast @base in EVERY palette are used: subtext/surface mean
|
||||
"on-surface" in some light themes (summer-day: subtext==base,
|
||||
surface==text — body text was invisible on hardware, item 25).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Nomarchy theming engine.
|
||||
#
|
||||
# nomarchy.stateFile (theme-state.json, inside the consuming flake) is
|
||||
# nomarchy.stateFile (state.json, inside the consuming flake) is
|
||||
# ingested at evaluation time — pure, because flake files are store
|
||||
# paths — and exposed to every other module as `config.nomarchy.theme`.
|
||||
#
|
||||
# Theme changes are fully Home Manager managed: `nomarchy-theme-sync
|
||||
# Theme changes are fully Home Manager managed: `nomarchy-state-sync
|
||||
# apply <theme>` writes the JSON and runs `home-manager switch`, baking
|
||||
# everything (Hyprland, Waybar, Kitty, btop, Stylix) into one
|
||||
# read-only generation. No runtime patching, no partial states; theme
|
||||
@@ -13,17 +13,17 @@
|
||||
# The one runtime exception is the wallpaper (swww is imperative by
|
||||
# nature): applied at session start, after every switch (hook below), and
|
||||
# after output hotplug (hyprland.nix); cycled instantly with
|
||||
# `nomarchy-theme-sync bg next`.
|
||||
# `nomarchy-state-sync bg next`.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy;
|
||||
|
||||
# Fail-closed load: missing / empty / non-object get a short pointer at
|
||||
# the template + `nomarchy-theme-sync validate`, not a raw readFile stack
|
||||
# the template + `nomarchy-state-sync validate`, not a raw readFile stack
|
||||
# from deep inside a consumer (nightlight, hyprland, …). Field-level
|
||||
# checks below still run on the merged result.
|
||||
themeState = import ../theme-state-read.nix { inherit lib; } cfg.stateFile;
|
||||
themeState = import ../state-read.nix { inherit lib; } cfg.stateFile;
|
||||
|
||||
# Defaults guarantee evaluation succeeds on a sparse or older state
|
||||
# file (e.g. one written before a schema field was added). The shipped
|
||||
@@ -92,7 +92,7 @@ let
|
||||
parsed = lib.recursiveUpdate defaults themeState;
|
||||
|
||||
# ── Friendly eval-time validation ───────────────────────────────────
|
||||
# The same schema nomarchy-theme-sync enforces before every write. A
|
||||
# The same schema nomarchy-state-sync enforces before every write. A
|
||||
# HAND-edited state file (the one path that bypasses the tool) must
|
||||
# fail with the field, the problem, and the fix — not a Nix stack
|
||||
# trace deep in some consumer. Checks run on `parsed` (after the
|
||||
@@ -137,14 +137,14 @@ let
|
||||
if problems == [ ] then parsed
|
||||
else throw ''
|
||||
|
||||
Nomarchy: your theme-state.json is invalid:
|
||||
Nomarchy: your state.json is invalid:
|
||||
${lib.concatMapStrings (p: " ✖ ${p}\n") problems}
|
||||
Fix the named field(s) in the theme-state.json of your flake
|
||||
checkout (usually ~/.nomarchy/theme-state.json — the store copy at
|
||||
Fix the named field(s) in the state.json of your flake
|
||||
checkout (usually ~/.nomarchy/state.json — the store copy at
|
||||
${toString cfg.stateFile} is a snapshot of it). The tool prints
|
||||
the same report with per-field fixes: `nomarchy-theme-sync
|
||||
the same report with per-field fixes: `nomarchy-state-sync
|
||||
validate`. Re-applying any preset resets all appearance fields:
|
||||
`nomarchy-theme-sync apply boreal`.'';
|
||||
`nomarchy-state-sync apply boreal`.'';
|
||||
|
||||
# A border value is a palette key (look it up in colors) unless it's
|
||||
# already a literal hex. Unknown roles are rejected by the field checks
|
||||
@@ -155,7 +155,7 @@ let
|
||||
|
||||
Nomarchy: border role "${v}" is not in the palette (colors.*).
|
||||
Use one of: ${lib.concatStringsSep ", " colorRoles}
|
||||
or a literal "#RRGGBB". Validate with: nomarchy-theme-sync validate'');
|
||||
or a literal "#RRGGBB". Validate with: nomarchy-state-sync validate'');
|
||||
border = {
|
||||
active = resolveColor checked.border.active;
|
||||
inactive = resolveColor checked.border.inactive;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# timezone. Waybar's clock module captures the zone once at construction, so a
|
||||
# runtime timezone change (automatic-timezoned, nomarchy.system.autoTimezone)
|
||||
# would NOT show until a relogin. A tiny watcher subscribes to timedate1's
|
||||
# change signal and reloads Waybar (SIGUSR2 = the same reload theme-sync uses),
|
||||
# change signal and reloads Waybar (SIGUSR2 = the same reload state-sync uses),
|
||||
# so the clock follows your location live. Also catches a manual
|
||||
# `timedatectl set-timezone`.
|
||||
#
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Waybar — two-tier theming:
|
||||
#
|
||||
# 1. Default: structure, fonts, geometry AND palette baked from
|
||||
# theme-state.json (colors as GTK named colors, @define-color).
|
||||
# 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
|
||||
@@ -35,7 +35,7 @@ let
|
||||
# Waybar supervisor — exec-once has no restart, so a crashed bar used to
|
||||
# leave the session bar-less until relogin (seen on hardware: a theme
|
||||
# switch crashed waybar mid-reload). Respawns on ANY exit — a plain
|
||||
# `pkill -x waybar` is now a clean restart, which nomarchy-theme-sync
|
||||
# `pkill -x waybar` is now a clean restart, which nomarchy-state-sync
|
||||
# uses instead of the crash-prone in-place SIGUSR2 reload when it sees
|
||||
# this supervisor running. Crash-loop guard: 5 exits within 10s of
|
||||
# their start → give up with a critical notification instead of
|
||||
@@ -377,7 +377,7 @@ let
|
||||
};
|
||||
|
||||
generatedStyle = ''
|
||||
/* Palette baked from theme-state.json */
|
||||
/* Palette baked from state.json */
|
||||
${colorDefs}
|
||||
|
||||
/* NB: this `*` reset reaches the SNI tray menus Waybar hosts too
|
||||
@@ -522,7 +522,7 @@ in
|
||||
# on a warm relogin — it started before the socket was up, exited, landed
|
||||
# in `failed`, and was never retried, so the bar vanished. exec-once only
|
||||
# fires once Hyprland is up, dodging the race; theme switches reload the
|
||||
# running bar via SIGUSR2 (nomarchy-theme-sync). No uwsm here to manage the
|
||||
# running bar via SIGUSR2 (nomarchy-state-sync). No uwsm here to manage the
|
||||
# session target, so we don't depend on its lifecycle.
|
||||
systemd.enable = false;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# yazi — the flagship file manager: a fast, keyboard-driven TUI that
|
||||
# fits the distro's identity (Kitty graphics previews, rofi
|
||||
# menus, everything from one JSON). Themed from theme-state.json and
|
||||
# menus, everything from one JSON). Themed from state.json and
|
||||
# shipped with a curated plugin set. The GUI half (Thunar, "open folder"
|
||||
# handler) is nomarchy.system.fileManager on the system side.
|
||||
#
|
||||
|
||||
@@ -24,11 +24,11 @@ let
|
||||
# Control Center's Bluetooth toggle writes settings.bluetooth.enable; read
|
||||
# it from the state file, the only place it exists on the NixOS side (the
|
||||
# hardware.nix/timezone.nix bridge). Missing/invalid JSON fails closed via
|
||||
# theme-state-read.nix rather than a raw stack. null = key absent, which
|
||||
# state-read.nix rather than a raw stack. null = key absent, which
|
||||
# leaves the option's own default (true) alone.
|
||||
sysState =
|
||||
if cfg.stateFile != null
|
||||
then import ../theme-state-read.nix { inherit lib; } cfg.stateFile
|
||||
then import ../state-read.nix { inherit lib; } cfg.stateFile
|
||||
else { };
|
||||
stateBluetooth =
|
||||
let v = (sysState.settings or { }).bluetooth.enable or null;
|
||||
@@ -70,7 +70,7 @@ in
|
||||
nomarchy-pull update flake inputs (nomarchy, nixpkgs, …)
|
||||
nomarchy-rebuild rebuild the system (current lock)
|
||||
nomarchy-home rebuild the desktop / Home Manager
|
||||
nomarchy-theme-sync apply <theme> switch the whole palette
|
||||
nomarchy-state-sync apply <theme> switch the whole palette
|
||||
nomarchy-doctor read-only health check
|
||||
SUPER+? keybindings cheatsheet
|
||||
SUPER+M → System › Firmware check LVFS firmware updates (fwupd)
|
||||
@@ -336,7 +336,7 @@ in
|
||||
# ── Fonts ────────────────────────────────────────────────────────
|
||||
# The ten most popular Nerd Fonts ship by default, so any of them
|
||||
# can be named in the theme state's fonts.mono and actually resolve
|
||||
# (nomarchy-theme-sync warns when a configured font is missing).
|
||||
# (nomarchy-state-sync warns when a configured font is missing).
|
||||
fonts = {
|
||||
packages = with pkgs; [
|
||||
nerd-fonts.jetbrains-mono
|
||||
@@ -373,7 +373,7 @@ in
|
||||
|
||||
# ── Essential packages ───────────────────────────────────────────
|
||||
environment.systemPackages = with pkgs; [
|
||||
nomarchy-theme-sync # provided by overlays.default
|
||||
nomarchy-state-sync # provided by overlays.default
|
||||
nomarchy-doctor # read-only health check (System › Doctor)
|
||||
nomarchy-control-center # TUI control center
|
||||
nomarchy-detect-hw # post-install hardware re-probe (HARDWARE.md §8)
|
||||
@@ -508,7 +508,7 @@ in
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
auto-optimise-store = lib.mkDefault true;
|
||||
# The downstream flake (~/.nomarchy) is meant to be a live working
|
||||
# tree: nomarchy-theme-sync rewrites theme-state.json on every
|
||||
# tree: nomarchy-state-sync rewrites state.json on every
|
||||
# switch (and you needn't commit each tweak), so the "Git tree is
|
||||
# dirty" warning fires on every rebuild and is pure noise here.
|
||||
warn-dirty = lib.mkDefault false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Greeter — greetd/tuigreet, themed from the same theme-state.json that
|
||||
# Greeter — greetd/tuigreet, themed from the same state.json that
|
||||
# drives the desktop (nomarchy.system.stateFile; the Plymouth model:
|
||||
# baked at SYSTEM rebuild, so it follows the theme as of the last
|
||||
# sys-update, not the last instant apply).
|
||||
@@ -27,7 +27,7 @@ let
|
||||
cfg = config.nomarchy.system;
|
||||
distroName = config.system.nixos.distroName;
|
||||
|
||||
sync = lib.getExe pkgs.nomarchy-theme-sync;
|
||||
sync = lib.getExe pkgs.nomarchy-state-sync;
|
||||
|
||||
# Menu/CLI toggle, same shape as nomarchy-autotimezone: runs as the normal
|
||||
# user (it owns the flake checkout + writes the state), sudos only the
|
||||
@@ -62,12 +62,12 @@ let
|
||||
fi
|
||||
'';
|
||||
|
||||
# Fails closed with an actionable message via theme-state-read.nix, like
|
||||
# Fails closed with an actionable message via state-read.nix, like
|
||||
# every other stateFile consumer — a raw fromJSON here would bury a bad
|
||||
# state file under a Nix stack pointing at greeter.nix.
|
||||
state =
|
||||
if cfg.stateFile != null
|
||||
then import ../theme-state-read.nix { inherit lib; } cfg.stateFile
|
||||
then import ../state-read.nix { inherit lib; } cfg.stateFile
|
||||
else { };
|
||||
|
||||
# The auto-login user from the state, or null. Read here via the state file
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.hardware;
|
||||
# Fingerprint PAM can follow theme-state.json (menu toggle → next
|
||||
# Fingerprint PAM can follow state.json (menu toggle → next
|
||||
# sys-rebuild), same bridge as autoTimezone (BACKLOG #55). Missing or
|
||||
# invalid JSON fails closed (theme-state-read.nix) instead of a raw stack.
|
||||
# invalid JSON fails closed (state-read.nix) instead of a raw stack.
|
||||
hwState =
|
||||
if config.nomarchy.system.stateFile != null
|
||||
then import ../theme-state-read.nix { inherit lib; } config.nomarchy.system.stateFile
|
||||
then import ../state-read.nix { inherit lib; } config.nomarchy.system.stateFile
|
||||
else { };
|
||||
pamFromState = (hwState.settings or { }).fingerprint.pam or false;
|
||||
|
||||
sync = lib.getExe pkgs.nomarchy-theme-sync;
|
||||
sync = lib.getExe pkgs.nomarchy-state-sync;
|
||||
|
||||
# The single fingerprint on/off switch (System › Fingerprint). One state key
|
||||
# for one user-facing decision — it drives login/sudo PAM here AND the
|
||||
@@ -156,11 +156,11 @@ in
|
||||
type = lib.types.bool;
|
||||
default = pamFromState;
|
||||
defaultText = lib.literalExpression
|
||||
"(settings.fingerprint.pam from theme-state.json) or false";
|
||||
"(settings.fingerprint.pam from state.json) or false";
|
||||
description = ''
|
||||
Use the fingerprint for login and sudo (PAM). Opt-in — password-only
|
||||
stays the default for the cautious; enroll a finger first. Defaults
|
||||
from theme-state.json `settings.fingerprint.pam` (System › Fingerprint
|
||||
from state.json `settings.fingerprint.pam` (System › Fingerprint
|
||||
menu) when set; otherwise false.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
defaultText = lib.literalExpression
|
||||
"(settings.greeter.autoLogin from theme-state.json) or null";
|
||||
"(settings.greeter.autoLogin from state.json) or null";
|
||||
example = "ada";
|
||||
description = ''
|
||||
Log this user straight into Hyprland on boot (greetd
|
||||
initial_session); logging out lands on the normal greeter.
|
||||
|
||||
Normally you leave this alone and use System › Auto-login, which
|
||||
writes `settings.greeter.autoLogin` in theme-state.json —
|
||||
writes `settings.greeter.autoLogin` in state.json —
|
||||
./greeter.nix mkDefaults this option from it. The installer seeds
|
||||
that state on LUKS-encrypted machines: the disk passphrase already
|
||||
gates access, so a second prompt is ceremony. Setting this option by
|
||||
@@ -30,15 +30,15 @@
|
||||
|
||||
plymouth.enable = lib.mkEnableOption ''
|
||||
the Nomarchy Plymouth boot splash (logo + progress + LUKS prompt),
|
||||
background-tinted from theme-state.json via nomarchy.system.stateFile.
|
||||
background-tinted from state.json via nomarchy.system.stateFile.
|
||||
Recolors on system rebuilds — theme switches don't touch the initrd'' // { default = true; };
|
||||
|
||||
stateFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = lib.literalExpression "./theme-state.json";
|
||||
example = lib.literalExpression "./state.json";
|
||||
description = ''
|
||||
theme-state.json for the system-side consumers (currently the
|
||||
state.json for the system-side consumers (currently the
|
||||
Plymouth splash background). lib.mkFlake wires it automatically
|
||||
from your flake; null falls back to the Boreal base color.
|
||||
'';
|
||||
@@ -59,7 +59,7 @@
|
||||
bluetooth.enable = lib.mkEnableOption "Bluetooth support with blueman" // {
|
||||
default = true;
|
||||
defaultText = lib.literalExpression
|
||||
"(settings.bluetooth.enable from theme-state.json) or true";
|
||||
"(settings.bluetooth.enable from state.json) or true";
|
||||
};
|
||||
|
||||
autoTimezone.enable = lib.mkEnableOption ''
|
||||
@@ -119,7 +119,7 @@
|
||||
batteryChargeLimit = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.ints.between 50 100);
|
||||
# No state bridge at eval time, by design: ./power.nix's oneshot reads
|
||||
# settings.power.batteryChargeLimit out of the live theme-state.json
|
||||
# settings.power.batteryChargeLimit out of the live state.json
|
||||
# with jq at *runtime* and prefers it over this baked value, so the
|
||||
# menu applies before (and without) a rebuild. This used to read
|
||||
# `config.nomarchy.settings…`, which does not exist on the NixOS side
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Plymouth boot splash — Nomarchy-branded (ported from the legacy
|
||||
# iteration), background tinted from the same theme-state.json that
|
||||
# iteration), background tinted from the same state.json that
|
||||
# drives the desktop (nomarchy.system.stateFile, wired automatically by
|
||||
# lib.mkFlake). One caveat by design: theme switches are Home
|
||||
# Manager-only and never touch the initrd, so the splash follows the
|
||||
# theme as of the last SYSTEM rebuild (`sys-update`), not the last
|
||||
# `nomarchy-theme-sync apply`.
|
||||
# `nomarchy-state-sync apply`.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
@@ -12,7 +12,7 @@ let
|
||||
|
||||
state =
|
||||
if cfg.stateFile != null
|
||||
then import ../theme-state-read.nix { inherit lib; } cfg.stateFile
|
||||
then import ../state-read.nix { inherit lib; } cfg.stateFile
|
||||
else { };
|
||||
colorOf = key: fallback: lib.removePrefix "#" ((state.colors or { }).${key} or fallback);
|
||||
# Fallbacks match the distro default theme (Boreal) when stateFile is null.
|
||||
|
||||
@@ -58,7 +58,7 @@ in
|
||||
# Instant menu path (user decision 2026-07-10): the threshold node is
|
||||
# group-writable for `users` so nomarchy-menu can echo live without
|
||||
# rebuild; this oneshot still owns boot + AC-replug re-apply. Prefer
|
||||
# live theme-state.json under /home/*/.nomarchy so a menu change
|
||||
# live state.json under /home/*/.nomarchy so a menu change
|
||||
# survives reboot before the next sys-rebuild bakes the Nix option.
|
||||
systemd.services.nomarchy-battery-charge-limit = lib.mkIf cfg.laptop {
|
||||
description = "Apply battery charge end threshold from state or config";
|
||||
@@ -80,7 +80,7 @@ in
|
||||
# Baked generation default (empty = full charge / no cap).
|
||||
limit=${if cfg.batteryChargeLimit != null then toString cfg.batteryChargeLimit else ""}
|
||||
# Prefer the newest live state write (menu path, no rebuild yet).
|
||||
for st in /home/*/.nomarchy/theme-state.json; do
|
||||
for st in /home/*/.nomarchy/state.json; do
|
||||
[ -r "$st" ] || continue
|
||||
v=$(jq -r '.settings.power.batteryChargeLimit // empty' "$st" 2>/dev/null || true)
|
||||
case "$v" in
|
||||
|
||||
@@ -11,11 +11,11 @@ let
|
||||
# Control Center's Printing toggle writes settings.printing.enable; read it
|
||||
# from the state file, the only place it exists on the NixOS side (the
|
||||
# hardware.nix/timezone.nix bridge). Missing/invalid JSON fails closed via
|
||||
# theme-state-read.nix rather than a raw stack. null = key absent, which
|
||||
# state-read.nix rather than a raw stack. null = key absent, which
|
||||
# leaves the option's own default alone.
|
||||
svcState =
|
||||
if config.nomarchy.system.stateFile != null
|
||||
then import ../theme-state-read.nix { inherit lib; } config.nomarchy.system.stateFile
|
||||
then import ../state-read.nix { inherit lib; } config.nomarchy.system.stateFile
|
||||
else { };
|
||||
statePrinting =
|
||||
let v = (svcState.settings or { }).printing.enable or null;
|
||||
@@ -97,7 +97,7 @@ in
|
||||
System ▸ Printers entry opens the system-config-printer GUI'' // {
|
||||
default = false;
|
||||
defaultText = lib.literalExpression
|
||||
"(settings.printing.enable from theme-state.json) or false";
|
||||
"(settings.printing.enable from state.json) or false";
|
||||
};
|
||||
|
||||
openrgb.enable = lib.mkEnableOption ''
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# /etc/localtime at runtime.
|
||||
#
|
||||
# In-flake state, menu-driven (the keyboard/night-light philosophy): the on/off
|
||||
# flag lives in the same theme-state.json under `settings.autoTimezone`
|
||||
# flag lives in the same state.json under `settings.autoTimezone`
|
||||
# (git-tracked, reproducible), written by the System-menu toggle
|
||||
# (nomarchy-autotimezone). Because this is a SYSTEM service — not a user unit it
|
||||
# can start/stop instantly like night-light — the toggle drives a system rebuild
|
||||
@@ -16,14 +16,14 @@ let
|
||||
|
||||
# Read the same state file the rest of the system side uses (Plymouth too),
|
||||
# wired by lib.mkFlake. The flag defaults off when stateFile is null;
|
||||
# a set-but-missing/invalid path fails closed via theme-state-read.nix.
|
||||
# a set-but-missing/invalid path fails closed via state-read.nix.
|
||||
state =
|
||||
if cfg.stateFile != null
|
||||
then import ../theme-state-read.nix { inherit lib; } cfg.stateFile
|
||||
then import ../state-read.nix { inherit lib; } cfg.stateFile
|
||||
else { };
|
||||
stateEnabled = (state.settings or { }).autoTimezone or false;
|
||||
|
||||
sync = lib.getExe pkgs.nomarchy-theme-sync;
|
||||
sync = lib.getExe pkgs.nomarchy-state-sync;
|
||||
|
||||
# Menu/CLI toggle. Runs as the normal user (it owns the flake checkout +
|
||||
# writes the state); sudos only the system switch, like sys-update. Writes
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Pure theme-state.json loader — fail closed with a short, actionable
|
||||
# Pure state.json loader — fail closed with a short, actionable
|
||||
# message instead of a raw `readFile` / `fromJSON` stack buried in a
|
||||
# consumer. Used by modules/home/theme.nix (required path), the NixOS
|
||||
# stateFile consumers, and lib.mkFlake (early gate).
|
||||
@@ -6,43 +6,49 @@
|
||||
# Field-level schema checks stay in theme.nix (post-defaults). This file
|
||||
# only gates *existence* and *JSON-shape* so the first failure the user
|
||||
# sees points at the state file, not at nightlight.nix.
|
||||
#
|
||||
# Callers may still pass a legacy path (state.json); messages name
|
||||
# whatever path they gave so a half-migrated checkout is still debuggable.
|
||||
{ lib }:
|
||||
|
||||
path:
|
||||
|
||||
let
|
||||
pathStr = toString path;
|
||||
baseName = baseNameOf pathStr;
|
||||
|
||||
tip = ''
|
||||
Fix:
|
||||
• Missing file → copy the template next to your flake.nix:
|
||||
templates/downstream/theme-state.json
|
||||
templates/downstream/state.json
|
||||
or regenerate from a preset:
|
||||
nomarchy-theme-sync apply boreal
|
||||
• Bad syntax / wrong shape → edit theme-state.json (trailing commas
|
||||
nomarchy-state-sync apply boreal
|
||||
• Bad syntax / wrong shape → edit ${baseName} (trailing commas
|
||||
are the usual culprit) or reset with `apply` as above.
|
||||
• Field-level schema (colors, ui, border, …):
|
||||
nomarchy-theme-sync validate
|
||||
Eval-time field checks live in modules/home/theme.nix.'';
|
||||
nomarchy-state-sync validate
|
||||
Eval-time field checks live in modules/home/theme.nix.
|
||||
• Still on theme-state.json? Rename it to state.json (or just run
|
||||
any menu write / `nomarchy-state-sync set` — it migrates).'';
|
||||
|
||||
missingMsg = ''
|
||||
|
||||
Nomarchy: theme-state.json is missing:
|
||||
Nomarchy: state file is missing:
|
||||
${pathStr}
|
||||
|
||||
This file is required (appearance + menu settings). Add it to your
|
||||
flake checkout so evaluation stays pure.
|
||||
This file is required (appearance + menu settings). Add state.json to
|
||||
your flake checkout so evaluation stays pure.
|
||||
${tip}'';
|
||||
|
||||
emptyMsg = ''
|
||||
|
||||
Nomarchy: theme-state.json is empty:
|
||||
Nomarchy: state file is empty:
|
||||
${pathStr}
|
||||
${tip}'';
|
||||
|
||||
notObjectMsg = ''
|
||||
|
||||
Nomarchy: theme-state.json must be a JSON object `{ ... }`:
|
||||
Nomarchy: state file must be a JSON object `{ ... }`:
|
||||
${pathStr}
|
||||
${tip}'';
|
||||
|
||||
Reference in New Issue
Block a user