Two low-severity edge bugs from a fresh-eyes review of the session batch:
- cmd_auto: an inverted schedule (sunrise > sunset, i.e. a day window that
crosses midnight) stayed perpetually on the day theme — the old
`not between` shortcut is an empty interval when sunrise > sunset.
Replaced with a real wrap-around: daytime = mins >= sunrise or
mins < sunset, so both directions switch. Normal sunrise < sunset path
is unchanged (checks.auto-theme still holds).
- rofi autotheme HH:MM validator: `[0-2][0-9]` accepted 24:00–29:59.
Tightened the case pattern to `[01][0-9]:[0-5][0-9]|2[0-3]:[0-5][0-9]`
so out-of-range hours are rejected (they'd otherwise skew the boundary).
(Two other review findings dismissed: papirus DOES ship breeze/breeze-dark
so that iconPacks prefix is correct; the doctor RAM-unknown row is cosmetic
since /proc/meminfo is always readable.)
Verification: py_compile; boundary truth-table (normal + wrap-around);
HH:MM case table (23:00/19:59/20:00 accept, 24/25/30:00 reject); auto
--which normal-case unchanged; menu bash -n; flake check green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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 <noreply@anthropic.com>
modules/home/autotheme.nix installs a user service + timer
(nomarchy-auto-theme) that periodically runs `nomarchy-theme-sync auto`,
so the day/night theme is applied on the current schedule through the
same one engine. Install gates on settings.autoTheme.enable (read from
state, the way updates.nix gates on its enable), so a machine not using
the feature carries no timer.
Timer: OnStartupSec=1min (settle after login) + OnCalendar=*:0/15 +
Persistent (catch a transition missed while off). Periodic rather than
exact sunrise/sunset timers — robust to suspend/DST, and `auto` is
idempotent (no rebuild unless the theme must change) so ticks are cheap.
Verification: V1 — real-config eval (state temporarily enabled, then
restored) confirms the units land when enabled (OnCalendar *:0/15,
ExecStart `... nomarchy-theme-sync auto`) and are absent when disabled;
systemd-analyze validates the schedule. V0 flake check green. The live
timer -> home-manager switch on a real Nomarchy session is pending a
Nomarchy machine (same blocker as #76).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
theme.nix now resolves nomarchy.theme.iconThemePackage from a small
icon-pack registry (Papirus/Tela/Qogir/Reversal/Numix, matched by name
prefix). Default themes name Papirus-*, so the resolved package is the
plain papirus-icon-theme derivation — byte-identical closure, zero added
MB. Only when a theme's `icons` names a set from another pack is that
pack (and only it) symlinkJoined alongside papirus. stylix.nix consumes
it via mkDefault. Adding a pack is one iconPacks row.
Deliberately NOT shipping more packs by default: papirus alone is already
~1 GiB and each extra pack adds ~hundreds of MB, so variety is opt-in per
install (set `icons` in theme-state.json), not a default closure cost.
Documented in templates/downstream/home.nix + ROADMAP § Icon themes.
Verification: V1 (HM activationPackage builds). Eval proofs — default
resolves to plain papirus (zero delta); icons="Tela-dark" ->
nomarchy-icon-themes symlinkJoin (papirus+tela) with the name flowing,
without building tela; all 5 pack attrs exist in the pinned nixpkgs;
template-sot + option-docs + flake check green. No default visual change
(same papirus), so no screenshot; the opt-in render is downstream.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per the settled #76 UX call: keep the Power-menu Hibernate row
unconditional, but when hibernate fails (typically a swap=0 machine with
no disk swap), surface a notification pointing at the enable-hibernation
runbook instead of a silent no-op. On success `systemctl hibernate`
blocks until resume, so the notify only fires on a real failure.
Verification: V1 — homeConfigurations.nomarchy.activationPackage builds
(rebuilds nomarchy-menu; writeShellScriptBin runs bash -n). Control-flow
proved on the arm extracted from the built script: failing systemctl ->
notify fires, succeeding -> no notify. flake check --no-build green.
V2 hibernate->resume VM test + V3 laptop remain open (#76 slice 3).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract nomarchy-pull/rebuild/home to pkgs/nomarchy-lifecycle; install
via system + home. Home profile wins on PATH so home-manager switch
installs the fixed pull (skip git when no upstream) without waiting
for a system rebuild that needed a working pull first.
V0: flake check --no-build green.
charge_control_end_threshold was written (80) but charge_types stayed
[Adaptive], which ignores the cap while still reporting it — battery
kept charging past 80% on Latitude. Oneshot now selects Custom when a
limit is set, Adaptive/Standard when off; sets start hysteresis; doctor
warns if type ≠ Custom while limited.
V0: flake check --no-build green. V3: re-apply limit, confirm
charge_types shows [Custom] and status stops Charging above cap.
Class xdg-desktop-portal-gtk (+ Nix .…-wrapped) from package desktop/
libexec. Polkit already V3 PASS on Latitude. NEXT only #20 remains.
V0: flake check --no-build green.
Two bare `{:%…}` specs emptied the hover tooltip (box with no text).
One strftime block with an embedded newline shows date + zone; ensure
tooltip label colors on generated + summer bars.
dmenu mode is case-sensitive by default (dmenu compat), so config.rasi
case-sensitive=false fixed apps/drun but not nomarchy-menu pickers.
rofi_menu helper always passes -i; networkmanager_dmenu gets rofi -i.
V0: flake check --no-build green. V3: type sys in root Menu → System.
Missing, empty, or non-object theme-state.json now throws a short
message pointing at the template and `nomarchy-theme-sync validate`
instead of a raw readFile/fromJSON stack. Shared reader:
modules/theme-state-read.nix — wired in theme.nix, plymouth/timezone/
hardware, and lib.mkFlake (seq-forced early gate). Field-level schema
in theme.nix kept; unknown border roles still fail there; resolveColor
no longer falls through on missing palette keys. Defaults added for
settings.displayProfile / displayProfileAuto.
Verified: V0 nix flake check --no-build; negative evals (missing,
empty, [], bad border role); good-path template mode + displayProfile
defaults.
Self-gated System › Fingerprint when fprintd is on PATH: enroll, list,
verify, delete-all (terminal), and Use for login writing
settings.fingerprint.pam (hardware.nix default from theme-state). Close#55.
Verified: V1 (HM menu rebuild). V3 pending: real-reader enroll (HARDWARE-QUEUE).
Always-visible install surface on the live ISO: xdg.desktopEntries
(Terminal=true) plus Tools › Install Nomarchy (self-gated on
nomarchy-install on PATH). Welcome toast mentions the app. Close#57.
Verified: V1 (live eval + HM menu rebuild). V2 pending: ISO smoke.
Add custom/nomarchy (left, Nomarchy monogram → nomarchy-menu) and
custom/powermenu (right → nomarchy-menu power) to the generated bar so
default-theme users match whole-swap affordances. Pin font-family:
Nomarchy; use literal UTF-8 glyphs (Nix has no \u escapes).
Verified: V2 (THEME=tokyo-night theme-shot — monogram + power glyph).
Close#63.
#54: permanent checks.installer-safety; test-install-safety harness with
offline compose-fail, LUKS-signature disk warn, and HM-hint FORCE paths.
#33: enable themed rofi scrollbars (generated + all whole-swap .rasi).
#43: MOTD + first-boot tip for System › Firmware (V3 LVFS stays hw-queue).
V2: compose-fail/disk-warn/hm-hint logs green offline; theme-shot
boreal+summer-night menus OK. V0 flake/installer-safety.
Doctor tripwire was self-hiding under waybar (PATH miss / weak glyph).
Call nomarchy-doctor by store path, strip ANSI tooltips, use + format
+ signal 10 (generated and whole-swap). theme-shot asserts class:bad,
guest btop.theme, and pokes RTMIN+10 before desktop shots.
V2: theme-shot green for rose-pine, summer-night, catppuccin, everforest,
vantablack; bar crops show the red alert-circle. btop TUI visual is
softGL-blocked (Ghostty) → HARDWARE-QUEUE. Closes#51 and #52.
Doctor Waybar indicator now emits a non-empty glyph () + class:bad when
unhealthy (was "text":"" → self-hidden, so the tripwire never showed);
Control Center appearance toggles (blur/gaps) print "(requires rebuild)"
matching the other CC toggles; summer-day workspace scroll e±1 → r±1 to
match every other bar.
Worktree agent; diff reviewed + cherry-picked. V0 flake check + V1
(HM generation, nomarchy-control-center build). V2 render folded into #44.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#50: Bluetooth menu row + handler self-gate on `command -v
blueman-manager` (no-op when bluetooth is off, like Printers); Control
Center rollback list /tmp/nomarchy-gens → mktemp; usbutils on the
nomarchy-install PATH (fingerprint lsusb probe); export
nomarchy-doctor/control-center/battery-notify as flake packages.
Verified V0 (flake check) + V1 (packages, installer, HM generation build).
Also records #46: the swap=0 + unattended-LUKS fail-closed contracts
(208b8d4) V2-verified via a headless two-variant install — fail-closed
exits before wipe with the disk blank; swap=0 installs with no @swap
subvol and boots to a themed desktop. Both items removed from NEXT.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a self-gated Firmware row to the System submenu (present whenever
fwupdmgr is on PATH, i.e. services.fwupd.enable — default-on), its
dispatch, and a `firmware` handler that opens a terminal and runs
fwupdmgr refresh → get-updates → y/N confirm → fwupdmgr update. Never
auto-flashes: fwupdmgr update confirms each device and prompts for the
reboot a capsule needs (pillar 1 — no silent BIOS writes). HARDWARE.md
§4 updated to "shipped".
Verified V0 (nix flake check --no-build) + V1 (HM generation builds;
generated nomarchy-menu passes bash -n; firmware handler emitted
correctly). V2 (menu render in a session) queued in #43 and V3 (real
LVFS flash) in HARDWARE-QUEUE — the VM was held by the #46 install test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The default desktop showed a red Hyprland config-error banner on every
boot, occluding the Waybar. Root cause: Hyprland 0.53 rewrote window
rules — the `windowrulev2` keyword is a hard error and the old
rule-first order (`float, class:^…$`) no longer parses, so the float
rules were not applying either. Confirmed against the Hyprland 0.55.4
source (`handleWindowrule`): each comma field is now `<keyword> <value>`,
so effects carry a value and matchers take a `match:` prefix.
Migrated the 7-rule block to `float 1, match:class ^…$` form.
Verified: V0 nix flake check --no-build; built the generated
hyprland.conf; V2 theme-shot re-render — banner gone, themed Waybar
renders. Journal + MEMORY gotcha added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Seed theme-state.json and the downstream template from themes/boreal.json
(including border + empty settings). Point eval/schema fallbacks at Boreal
(theme.nix preset, plymouth and live-ISO splash colors, theme-shot, sync
error examples). Docs/TESTING and RECOVERY follow the new default.
Verified: state matches boreal palette; theme-contrast all pass.
preview.png for boreal still missing (picker plain-name; V3 capture).
- Fixed invalid `windowrule` syntax when matching by class regex
which caused Hyprland parse errors.
- Documented IPC socket watcher for display profiles in README.
- Updated agent/JOURNAL.md.
Added satty (Wayland-native screenshot annotation tool) and integrated it with the Capture flow.
Wired `$mod SHIFT, Print` to slurp a region and open satty.
Added "Annotate region" to the rofi Capture menu (Tools > Capture).
Added satty's configuration via home-manager to inject the active theme's palette colors into its UI.
Verified: V1 (flake check --no-build, home activation package build).
Pending: V3 hardware/visual check for the satty UI (queued in HARDWARE-QUEUE.md).
Changed home.stateVersion assignment to use lib.mkDefault instead of a plain assignment.
This fixes downstream flakes that need to migrate existing NixOS installations without bumping their state version.
Verified: V1 (flake check --no-build, home activation package build).
Pending: nothing.
Item 28 color-only accessibility sweep. Swept every Waybar status
indicator for shape/glyph redundancy: all already carry non-color
distinction (updates shows a count + self-hides; vpn/doctor/nightlight/
recording self-gate; battery/idle/muted are glyph-distinct) except the
swaync notification module — its `inhibited-*` states reused the normal
bells (/), so an app-inhibited (suppressed) state was distinguished
from a normal one by color alone.
Route every suppressed state (DND + inhibited) to the bell-off glyph
+ @muted, so "notifications are off right now" reads by SHAPE. Applied
to the generated bar and all four whole-swaps (summer-day/night use
their own bell-off glyph; executive-slate/boreal use ) — parity rule.
Reuses the glyph DND already ships, so no new codepoint / tofu risk.
Verified: V0 (flake check --no-build; 4 jsoncs valid JSON) + V1 (built
homeConfigurations.nomarchy.activationPackage — the built config has
inhibited-* = U+F009B and style.css routes .inhibited-* → @muted).
V3 pending: live inhibited-state render with an app holding an inhibitor
(HARDWARE-QUEUE). MEMORY gains the "status is never color-only" invariant.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Item 42, de-scoped by Bernardo to app-only — no account sync (the user
configures that themselves locally). Clicking the Waybar date/clock now
runs nomarchy-calendar → calcurse in a floating, centered ghostty window
tagged --class=com.nomarchy.calendar (+ --gtk-single-instance=false for a
fresh standalone window), matched by a float+size+center windowrule. The
launcher self-gates on calcurse. Dropped the clock's format-alt date
toggle (conflicts with on-click) and moved the long date into the tooltip.
Parity: on-click added to all four whole-swap jsoncs.
Ships calcurse + pwvucontrol uncommented in the downstream template
(opt-out). Enforces ghostty always installed (ghostty.nix: enable = true
unconditional; nomarchy.ghostty.enable now gates only the theming/config)
so the --class launcher can rely on it.
Verified: V0 green; four jsoncs valid JSON; V1 HM built — launcher ships,
generated clock renders on-click=nomarchy-calendar (format-alt gone, date
in tooltip), hyprland has the 3 calendar windowrules, ghostty in the HM
profile. Click→float→calcurse month view is V3 (queued). Sync is
deliberately the user's own config, not a distro feature.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Item 36(b). Adds `nomarchy-menu powermgmt` — one flat rofi menu of power
Profile rows (→ powerprofilesctl set) + Charge limit rows (→ the same
theme-sync writer batterylimit uses), each half self-gating on its
hardware. No backend duplication; Custom charge stays in the dedicated
batterylimit picker.
Opened two ways (Bernardo's call): the Waybar battery icon AND the
power-profile icon — the profile icon's click flips from cycle → menu.
Wired in waybar.nix and all four whole-swaps (parity). Named `powermgmt`
to avoid the whole-swaps' existing `custom/powermenu` shutdown button
(→ nomarchy-menu power), which is left untouched. The granular System ▸
Power profile / Battery limit rows stay ("keep both").
nomarchy-powerprofile-cycle kept as a standalone bin, now unbound by
default.
Verified: V0 green; V1 HM built (bash -n) — powermgmt case ships, the
generated waybar-config.json wires both icons (2 hits), all four jsoncs
re-wired and still parse, shutdown buttons intact. Click→menu→apply is
V3 (queued).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Iteration #66 (direct request: "make sure menu items that have a keybind
display it"). Audit found every SUPER(+CTRL) module-opening row already
hinted (item 28c); the one gap was Tools ▸ Capture — its Region/Full
screen rows run the exact grim commands of the Print / SHIFT+Print /
CTRL+Print binds but showed nothing. Adds `hintForBind mods key` (matches
keybinds.nix by mods+key, so no drift and no brittle full-action compare)
and `-markup-rows` on the Capture menu so the spans render.
Verified: V0 green; V1 HM built — shipped nomarchy-menu shows
`Region → clipboard {{Print}}`, `Region → file {{SHIFT + Print}}`,
`Full screen → file {{CTRL + Print}}`. Substring case-matches precede the
appended span, so dispatch is unchanged. V3 (pango dim render) queued.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Item 32. Item 26 added SHIFT to the modmask but kept the shifted keysym
`question`, so the bind stayed dead. Hyprland 0.55 resolves a bind's
keysym with SHIFT consumed (issue #7750, behaviour since 0.43): with
Shift held, `$mod SHIFT, question` is matched against `slash` and never
fires. The base keysym is what works — same pattern as this file's own
`$mod SHIFT, 1` workspace binds. Also updates rofi.nix prettyKeys to
collapse "SHIFT + /" → "?" so the cheatsheet row still reads "SUPER + ?".
Verified: V0 `nix flake check --no-build` green; V1 HM activation built,
built hyprland.conf carries `bind=$mod SHIFT, slash, exec, nomarchy-menu
keybinds` (kb_layout=us → slash is `?`). The keypress opening the menu is
V3 — queued (HARDWARE-QUEUE, existing SUPER+? entry updated).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a `batterylimit` nomarchy-menu subcommand — a preset picker
(80/90/60/Off/Custom…) that writes settings.power.batteryChargeLimit via
theme-sync (--no-switch; lands on the next sys-rebuild, power.nix's oneshot
applies it). Surfaced as a first-class System row, self-gated on the
charge_control_end_threshold sysfs node like Power profile.
Moved out of the gum control-center (removed its Battery Limit case +
choose entry) so the toggle lives in one place — the rofi menu Bernardo
actually uses, per his request.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conservative first cut of the floating-window audit (item 41): float and
center the two shipped, unambiguous config dialogs — blueman
(manager/adapters) and system-config-printer — alongside the mixer. Uses
(?i) class regexes tolerant of the XWayland `.…-wrapped` form, consistent
with the existing (?i) usage in this file.
Deliberately conservative: network is dmenu/nmtui (no GUI editor), and the
polkit prompt + GTK file portals are deferred because their window class
can't be verified headlessly — a guessed class is a dead rule. Those wait
for a hardware `hyprctl clients` read (queued).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Right-clicking the Waybar volume module now opens pwvucontrol (per-app
mixer); left-click still mutes. Adds:
- on-click-right in waybar.nix + all four whole-swap jsoncs (parity)
- hyprland.nix: the config's first windowrule block — float + center the
mixer (pwvucontrol, with pavucontrol as a fallback class); normal-
priority list so a downstream windowrule concatenates
- pwvucontrol in the template home.packages
pwvucontrol is PipeWire-native + GTK4/libadwaita, so it themes via the
portal color-scheme like Amberol. app-id verified from the built package.
This settles the windowrule pattern/placement; item 41 is now just
broadening the float set. Right-click→float→center is a V3 session check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swap the vim-style SUPER+H/J/K/L focus binds for SUPER+arrow keys
(movefocus l/r/u/d). SUPER+arrows were unbound and there are no vim
move-window binds, so no collision. prettyKeys renders the arrow keysyms
as glyphs so the SUPER+? cheatsheet shows "SUPER + <-" not "SUPER + left".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mpv (video) and amberol (audio) are both already in the template suite;
the gap was mime wiring — audio/* still resolved to mpv.desktop. Repoint
the audio types to io.bassi.Amberol.desktop (id verified against the
built package) and broaden coverage to mp3/flac/ogg/opus/wav/m4a/aac,
both canonical and x- names. Video stays on mpv. Degrades to mpv if a
downstream drops Amberol from the suite.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>