refactor(#107): theme-state.json → state.json, theme-sync → state-sync
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:
2026-07-15 11:26:59 +01:00
parent 013403deb4
commit d8e1a13d50
62 changed files with 463 additions and 369 deletions

View File

@@ -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