feat(nightlight): menu-driven, in-flake on/off (no ~/.local/state)

Night light is off by default and configured entirely through the menu,
writing into the downstream flake instead of ~/.local/state — so the
choice is git-tracked and reproducible across a clone+rebuild.

Two flags under a new settings.* section of the state file (exposed as
the nomarchy.settings option):
- settings.nightlight.installed — sticky, gates the hyprsunset unit;
  nomarchy.nightlight.enable mkDefault-reads it. First enable from the
  menu rebuilds to create the unit (the one accepted rebuild).
- settings.nightlight.on — runtime on/off, toggled instantly
  (theme-sync set --no-switch + systemctl), no rebuild. An ExecCondition
  reads the live flag at session start so an off survives reboot;
  splitting it from the sticky flag means a later unrelated rebuild
  never undoes an instant-off (no decay).

Drops the ~/.local/state/nomarchy/nightlight-off marker and the
ConditionPathExists hack. theme-sync set now skips the wallpaper
re-apply for non-wallpaper keys, and its rebuild notifications are
generic ("changes" not "theme").

First cut of the broader principle — any user-settable config gets a
menu writer that lands it in the flake; no state outside the checkout.
Docs (README §4, ROADMAP, template) updated to reflect it.

Eval- and round-trip-verified; on-hardware check pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-06-21 20:24:58 +01:00
parent c8d0b09044
commit 685126ab47
10 changed files with 144 additions and 75 deletions

View File

@@ -165,12 +165,12 @@ def run_switch() -> None:
# Persistent (timeout 0): stays up for the whole rebuild — replaced
# in place by the success/failure notification below — so a multi-
# minute switch never looks like it silently failed.
notify("Applying theme — rebuilding the desktop…", persistent=True)
notify("Applying changes — rebuilding the desktop…", persistent=True)
result = subprocess.run(argv) # stream output to the caller's terminal
if result.returncode != 0:
notify("Theme rebuild FAILED — see terminal / journal", urgency="critical")
notify("Rebuild FAILED — see terminal / journal", urgency="critical")
die("rebuild failed (state file already updated; fix and re-run)")
notify("Theme applied ✓")
notify("Changes applied ✓")
# Waybar runs from Hyprland's exec-once (not a systemd unit HM would restart
# on switch), so nudge the running bar to re-read its freshly rebuilt
# config/style. SIGUSR2 = reload; harmless no-op if waybar isn't running.
@@ -294,7 +294,11 @@ def cmd_set(args) -> None:
check_fonts(state)
if not args.no_switch:
run_switch()
apply_wallpaper(state)
# Only wallpaper/theme keys change what swww shows; skip the re-apply
# (and its transition) for unrelated keys like ui.* or settings.* so a
# gaps tweak or a feature toggle doesn't flash the background.
if args.path.split(".")[0] in ("wallpaper", "slug"):
apply_wallpaper(state)
def cmd_get(args) -> None: