From 860c70466ff2c518356d705414a26bbcb94be71b Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Fri, 10 Jul 2026 21:53:37 +0100 Subject: [PATCH] =?UTF-8?q?feat(menu):=20#79=20slice=203=20=E2=80=94=20Loo?= =?UTF-8?q?k=20&=20Feel=20=E2=80=BA=20Auto=20theme=20(completes=20#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an "Auto theme (on/off)" row to Look & Feel and an `autotheme` submenu: toggle the feature, pick the day/night themes (from `nomarchy-theme-sync list`), and set the sunrise/sunset times (HH:MM, validated). This is the menu surface over settings.autoTheme, completing the auto time-of-day theme feature (slices: CLI / timer / menu). Enabling writes the flag and defaults the summer-day/summer-night pair --no-switch, then runs `auto --force`, so a single rebuild both installs the timer and applies the correct theme for now. Disabling is instant (--no-switch): the flag flips and `auto` self-gates, so the lingering timer no-ops until the next rebuild. Day/night/time writes are live --no-switch (read by `auto` at tick time). Verification: V1 — HM generation builds (bash -n on nomarchy-menu at build) + bash -n on the built menu. Logic test with stubbed rofi/theme-sync/notify: selecting the toggle from off runs exactly `set day/night/enable ... --no-switch` then `auto --force` — the intended one-rebuild path. V0 flake check green. All three #79 slices shipped; the live timer switch across sunset is a real-session (V3) check on a Nomarchy machine. Co-Authored-By: Claude Opus 4.8 --- agent/BACKLOG.md | 38 +++++++++++-------------- agent/JOURNAL.md | 21 ++++++++++++++ docs/ROADMAP.md | 14 +++++++++ docs/VISION.md | 2 +- modules/home/rofi.nix | 66 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 23 deletions(-) diff --git a/agent/BACKLOG.md b/agent/BACKLOG.md index 35425f4..fc558d3 100644 --- a/agent/BACKLOG.md +++ b/agent/BACKLOG.md @@ -28,28 +28,22 @@ in [`docs/ROADMAP.md`](../docs/ROADMAP.md); map in ## NEXT -### 79. Auto time-of-day theme pair (`VISION § D`) `[big]` — split -Auto light/dark theme switch through the **existing** engine (a timer runs -`nomarchy-theme-sync apply`), one pipeline. State: `settings.autoTheme` -`{enable, day, night, sunrise, sunset}` in theme-state.json. Slices: -1. ~~**CLI primitive + schema**~~ ✓ shipped 2026-07-10 — - `nomarchy-theme-sync auto` resolves day/night from - `settings.autoTheme.{enable,day,night,sunrise,sunset}` and applies (only - if different — idempotent, so the timer won't needlessly rebuild); - `--which` prints the decision, `--force`/`--no-switch` too. `cmd_apply` - core factored into `apply_named` (behavior unchanged). V1 + functional - tests (day/night/disabled/missing-slugs, apply writes state, idempotent). -2. ~~**Timer/hook**~~ ✓ shipped 2026-07-10 — `modules/home/autotheme.nix`: - `systemd.user.{service,timer}.nomarchy-auto-theme`, install gated on - `settings.autoTheme.enable` (read from state), `OnStartupSec=1min` + - `OnCalendar=*:0/15` + `Persistent`. Chose periodic (not exact - sunrise/sunset timers) — robust to suspend/DST, and `auto` is idempotent - so ticks are cheap. V1: real-config eval shows units land when enabled - (OnCalendar `*:0/15`, ExecStart `… auto`), absent when disabled; - systemd-analyze validates the schedule. **Real-session check pending** - (live timer→`home-manager switch`) — needs a Nomarchy machine, like #76. -3. **Menu** — Look & Feel › Auto theme: enable/disable, pick day/night - themes, set times. Writes state. +### 79. Auto time-of-day theme pair — all agent work shipped `[blocked:hw]` +Auto light/dark theme switch through the existing engine, one pipeline +(`VISION § D`). All 3 slices shipped 2026-07-10; design record in ROADMAP. +Only the real-session check remains (needs a Nomarchy machine, like #76): +- **CLI** — `nomarchy-theme-sync auto` (idempotent day/night apply; + `--which`/`--force`/`--no-switch`); `cmd_apply` core → `apply_named`. +- **Timer** — `modules/home/autotheme.nix`: user service+timer, install + gated on `settings.autoTheme.enable`, `OnStartupSec=1min` + + `OnCalendar=*:0/15` + `Persistent`. +- **Menu** — Look & Feel › Auto theme: toggle, pick day/night, set times + (enable rebuilds to install the timer + `auto --force`; disable is + instant `--no-switch`; day/night/times live `--no-switch`). + +**V3 remaining:** on a real Nomarchy session — enable from the menu, confirm +the timer switches day↔night across the sunset/sunrise boundary (and the +live `home-manager switch` from the timer succeeds). ### 76. Hibernation default — all agent work shipped `[blocked:hw]` Hibernation + zram on by default (product intent, Bernardo 2026-07-10). diff --git a/agent/JOURNAL.md b/agent/JOURNAL.md index 0e84403..6a26628 100644 --- a/agent/JOURNAL.md +++ b/agent/JOURNAL.md @@ -17,6 +17,27 @@ Template: --- +## 2026-07-10 — #79 slice 3: Look & Feel › Auto theme menu (#79 complete) +- **Task:** BACKLOG #79 slice 3 — the menu; completes the feature. +- **Did:** `rofi.nix` — "Auto theme (on/off)" row in Look & Feel + an + `autotheme)` submenu: toggle, pick day/night (from + `nomarchy-theme-sync list`), set sunrise/sunset (HH:MM input, validated). + Enable writes the flag + defaults the summer pair `--no-switch`, then + `exec auto --force` — one rebuild installs the timer AND applies the right + theme. Disable is instant `--no-switch` (`auto` self-gates; the lingering + timer no-ops). Day/night/times are live `--no-switch`. +- **Verified:** **V1** — HM generation builds (`writeShellScriptBin` bash -n + on nomarchy-menu passes) + bash -n on the built menu. **Logic test** + (stubbed rofi/theme-sync/notify): selecting "Auto theme (off)" runs exactly + `set day summer-day`, `set night summer-night`, `set enable true` + (all `--no-switch`), then `auto --force` — the intended one-rebuild path. + Disable path is static-clear (set false `--no-switch`, no `auto`); a + stateful-stub dynamic test would loop (stub doesn't reflect the flip). V0 + flake check green. **#79 collapsed to `[blocked:hw]`** (all 3 slices done; + only live-session switch remains, needs a Nomarchy machine). +- **Pending:** V3 real-session (timer switches day↔night across sunset). +- **Next suggestion:** NEXT head #76/#79 both `[blocked:hw]`; QA/PROPOSED. + ## 2026-07-10 — #79 slice 2: auto-theme systemd timer - **Task:** BACKLOG #79 slice 2 — the timer/hook. - **Did:** `modules/home/autotheme.nix` (imported in home default.nix): diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 3b7380e..07a8c6b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -286,6 +286,20 @@ Design/decision records and a running log of shipped work (items marked **Deliberately NOT shipping more packs by default** (Bernardo 2026-07-10): papirus alone is already ~1 GiB and each extra pack adds ~hundreds of MB, so variety is opt-in per install, not a default closure cost. +- ✓ **Auto time-of-day theme (#79, `VISION § D`):** an automatic light/dark + theme switch through the **same** engine as a manual apply — no second + pipeline. `settings.autoTheme = {enable, day, night, sunrise, sunset}` + drives `nomarchy-theme-sync auto`, which picks day vs night by the local + clock and applies the preset **only if it differs** (idempotent). + `modules/home/autotheme.nix` installs a user timer + (`OnStartupSec` + `OnCalendar=*:0/15` + `Persistent`, gated on the state + flag) that ticks `auto`; Look & Feel › Auto theme toggles it and picks the + pair/times. `cmd_apply`'s core was factored into `apply_named` so `auto` + reuses it verbatim. Periodic (not exact sunrise/sunset timers) for + robustness to suspend/DST; the idempotent `auto` keeps ticks cheap. Enable + rebuilds once (installs the timer + `auto --force`); disable is instant + (the flag flips and `auto` self-gates). Live timer→switch is a real-session + (V3) check. - **Nicer shell out of the box:** ✓ zsh is the default login shell, with a starship prompt themed from the JSON, autosuggestions + syntax highlighting, and modern-CLI ergonomics — `cat`→bat (theme "ansi", so it diff --git a/docs/VISION.md b/docs/VISION.md index 50f489e..abaeb67 100644 --- a/docs/VISION.md +++ b/docs/VISION.md @@ -92,7 +92,7 @@ Reference: [HARDWARE.md](HARDWARE.md) §4–§5, §10. | Idea | Intent | Notes | |------|--------|-------| | **Fewer themes, sharper** | Finish or quarantine neon-glass; invest in whole-swap quality | Contrast stays gated | -| **Time-of-day pair** | Auto light/dark (e.g. summer-day ↔ summer-night) from schedule | Still one theme engine | +| **Time-of-day pair** ✓ | Auto light/dark (e.g. summer-day ↔ summer-night) from schedule | ✓ shipped #79 (2026-07-10) — `settings.autoTheme` + timer; still one engine | | **Theme switch speed** | Wallpapers artifact split (LATER) if switches still feel slow | GOALS: no second pipeline | --- diff --git a/modules/home/rofi.nix b/modules/home/rofi.nix index e32fd8d..1b05812 100644 --- a/modules/home/rofi.nix +++ b/modules/home/rofi.nix @@ -603,10 +603,15 @@ ${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 ] + then row "Auto theme (on)" preferences-desktop-theme + else row "Auto theme (off)" preferences-desktop-theme + fi back } | rofi_menu -show-icons -markup-rows -p "Look & Feel") || exit 0 case "$choice" in "$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 ;; @@ -694,6 +699,67 @@ ${themeRows} && exec nomarchy-nightlight toggle notify-send "Night light" "Not available (nomarchy.nightlight off?)."; exit 0 ;; + autotheme) + # Look & Feel › Auto theme — automatic day/night theme switch + # (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 + # (--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; } + en=$(at_get enable); day=$(at_get day); night=$(at_get night) + sunrise=$(at_get sunrise); sunset=$(at_get sunset) + choice=$( { + if [ "$en" = true ] + then row "Auto theme (on)" preferences-desktop-theme + else row "Auto theme (off)" preferences-desktop-theme + fi + row "Day theme: ''${day:-— pick}" weather-clear + row "Night theme: ''${night:-— pick}" weather-clear-night + row "Sunrise: ''${sunrise:-07:00}" preferences-system-time + row "Sunset: ''${sunset:-20:00}" preferences-system-time + back + } | rofi_menu -show-icons -markup-rows -p "Auto theme") || exit 0 + case "$choice" in + "$BACK") exec "$0" lookfeel ;; + *"Auto theme"*) + if [ "$en" = true ]; then + nomarchy-theme-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 + notify-send "Auto theme" "On — applying the theme for now…" + exec nomarchy-theme-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" != "$BACK" ] && [ -n "$slug" ] \ + && nomarchy-theme-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" != "$BACK" ] && [ -n "$slug" ] \ + && nomarchy-theme-sync --quiet set settings.autoTheme.night "$slug" --no-switch + exec "$0" autotheme ;; + "Sunrise"*|"Sunset"*) + key=sunrise; label=Sunrise; cur="''${sunrise:-07:00}" + case "$choice" in "Sunset"*) key=sunset; label=Sunset; cur="''${sunset:-20:00}" ;; esac + t=$(rofi_menu -p "$label HH:MM (now $cur)" < /dev/null) || exec "$0" autotheme + case "$t" in + [0-2][0-9]:[0-5][0-9]) + nomarchy-theme-sync --quiet set "settings.autoTheme.$key" "$t" --no-switch ;; + "") : ;; + *) notify-send "Auto theme" "Ignored '$t' — use HH:MM (24-hour)." ;; + esac + exec "$0" autotheme ;; + esac ;; + autotimezone) # Toggle automatic timezone detection (geoclue + automatic-timezoned). # A SYSTEM service, so flipping it writes the in-flake flag and runs a