Every submenu's "↩ Back" row rendered two arrows: the ↩ glyph in the
label plus a themed go-previous icon. back() now emits the plain label
only — identical to what pick-lists already append — so Back shows a
single glyph everywhere. BACK="↩ Back" stays the universal label/match,
so no case-branch comparison changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Apps launcher (`rofi -show drun -theme launcher`) imported
`custom.rasi` — the name HM gives *generated* themes — but a whole-swap
theme's base is deployed under its source basename (`rofi.rasi`), so the
import resolved against $HOME and rofi errored with "custom.rasi not
found" on every whole-swap theme. Make the default import track the
active base (rofi.rasi for whole-swaps, custom.rasi otherwise) and point
Boreal's own launcher.rasi at rofi.rasi.
Also give Boreal's app grid `flow: horizontal` so Down at a column's foot
pages to the next screen instead of jumping to the next column's top,
mirroring the theme-picker grid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SUPER+Space/D open `rofi -show drun -theme launcher`, but the main menu's
"Apps" entry still ran bare `rofi -show drun`, so that path skipped the
per-theme launcher override (e.g. Boreal's icon grid) and fell back to the
list. Same theme for both launch sites now — consistent launcher whichever
way it's opened.
V0: flake check green; no bare `rofi -show drun` launch sites remain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Boreal — a Nord × Everforest × Dracula dark theme: a boreal forest at
night under the aurora. Nord's frost bones, Everforest's comfort, Dracula's
jewel-tone violet/cyan as the aurora light.
Palette (WCAG-checked, all 24 themes × 7 pairings pass): frost-slate base,
soft-vivid violet accent (#B79BE8, 6.35 on base) + frost-cyan accentAlt
(#86C7C0, 7.84 AAA), sage-green/gold/coral semantics, text 10.66 AAA.
Bespoke, non-standard surfaces:
- Waybar whole-swap (jsonc + css): a floating "aurora frost" bar of three
frosted-glass pills; launcher+clock left, workspaces centre, and the whole
right side is ONE continuous pill — the system tray shares its glass with
the status modules, no separators (the seamless-tray requirement). Full
module set (parity). GeistMono.
- Rofi: a frosted list base (used by every menu), plus a 4×3 large-icon grid
for the app launcher only.
- Typography GeistMono Nerd Font (57 MB, new); Iosevka was rejected at 1.1 GB.
Per-theme appearance (new): a preset now carries a full fonts+ui block, like
border, so a theme can ship a bespoke font / terminal opacity / border
thickness / rounding and a switch always replaces it — no leak into the next
theme (verified: boreal→everforest resets mono/rounding/opacity). All 23 other
presets get the current defaults (idempotent — zero visual change). Boreal
runs GeistMono + 0.90 terminal opacity + 3px borders + 12px rounding.
Grid launcher (new, backward-compatible): drun renders via `-theme launcher`;
HM writes ~/.local/share/rofi/themes/launcher.rasi which defaults to
`@import "custom.rasi"` (every other theme's launcher = its list, unchanged),
and a theme may override it (Boreal's grid). Text menus keep the base list.
Wallpaper: an original procedurally-generated aurora-over-treeline (ImageMagick),
palette-matched.
V1: nix flake check --no-build green; strict JSON on every preset; contrast
checker green; the real rofi binary parses the base rasi and resolves
`-theme launcher` + @import + grid overlay; end-to-end eval with boreal active
resolves the waybar whole-swap (tray in the right cluster), the rofi grid
launcher, and the bespoke fonts/opacity/geometry reaching nomarchy.theme, with
no cross-theme contamination. NOT done: on-screen render of the desktop
(V2/V3) and preview.png (deferred to on-hardware capture).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tools › Capture gains "OCR region → clipboard", self-gated on
tesseract: slurp the region (Esc cancels silently, the recording.nix
pattern), grim it, tesseract stdin→stdout, wl-copy. Whitespace-only
recognition toasts "No text recognized" instead of clobbering the
clipboard; success toasts the word count. tesseract ships English-only
through the menu's package set — the unscoped wrapper pulls every
language's traineddata, a real cost on a compile-from-source distro;
overriding with more languages is documented at the package line.
Verified: V0; V1 — template-home builds with the gated row + case and
menu bash -n; functional — the profile's own tesseract read a rendered
test image back verbatim, proving the OCR half of the pipe. The
grim/slurp half needs a session → HARDWARE-QUEUE.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The gate was >=3 appearance entries and it's met: the root's Theme row
becomes "Look & Feel" (pango-escaped & — the root passes
-markup-rows; matched on *Look*), a new lookfeel submenu carries the
Theme grid (SUPER+T hint), Next wallpaper (SUPER+SHIFT+T hint via the
new generalized hintForAction, which menuHint is now sugar over), and
the Night light toggle moved out of System — which thins by one row.
The root stays at six entries; both direct binds still hit the leaves.
Verified: V0; V1 — rendered menu passes bash -n with the mode, escaped
label and relocated night-light row; visual — tokyo-night 1080p
capture renders "Look & Feel" correctly with hints intact. Submenu
flow queued for a session test (HARDWARE-QUEUE).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nomarchy.launchOrFocus (opt-in, commented template example): each
entry gives a key that FOCUSES the app's existing window
(case-insensitive class match via jq + a "(?i)" focuswindow regex) or
launches it when none is open. One shared nomarchy-focus-or-launch
script; a bind whose command was removed from the suite fires a
"not installed" toast instead of failing silently. The same entries
render into the SUPER+? cheatsheet through prettyKeys — bind and
documentation stay one source. README row added.
Verified: V0; V1 — a scratch downstream with two entries renders both
binds (default command = lowercased class; custom mods/command/desc
honored), the script passes bash -n with a usage guard, and the
cheatsheet carries both rows. Focus behaviour needs a session -> V3
(HARDWARE-QUEUE).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit flagged the root picker as six short rows in ~760px of
empty width; the space now carries discoverability. menuHint renders
a dimmed pango span (size small, 55% alpha) from the SAME keybinds.nix
the Hyprland binds and the SUPER+? cheatsheet come from (prettyKeys),
so hints can never drift. Applied to the root picker (Apps special-
cased — it's rofi drun), all Tools leaves, and the System rows with
direct binds; those menus pass -markup-rows. Contains-glob matching
and the exact "↩ Back" row are unaffected.
Verified: V0; V1 (rendered menu bash -n, spans + 3x -markup-rows
present); visual — tokyo-night 1080p capture shows the hints dimmed
and subordinate on normal and selected rows alike.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nomarchy-display-profile gains `match <outputs>`: an exact set match
wins, else the largest profile whose named outputs are all connected;
any tie prints nothing — the watcher must never flap between ambiguous
layouts. nomarchy-display-profile-watch (exec-once when profiles are
declared, the keyboard-watcher poll pattern — session units race
Hyprland IPC on relogin) reacts to output-set CHANGES only, reads
settings.displayProfileAuto from the live state each time (the menu's
new Auto-switch row toggles it instantly, no rebuild), and applies via
the tool — so an auto-pick persists a concrete profile that the next
rebuild bakes. A separate boolean rather than an "auto" pseudo-profile
for exactly that bake semantics.
Verified: V0 (flake check); V1 — a scratch 4-profile downstream builds;
tool + watcher pass bash -n; the match matrix exercised end-to-end:
three exact picks, an unambiguous-superset pick, a tie -> none, a
no-fit -> none; the watcher is exec-once'd in the rendered config; the
menu renders the Auto-switch row. Remains: V3 real dock/undock
(HARDWARE-QUEUE, rides the slice-a entry). Slice c (workspace binding)
stays, tagged optional.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nomarchy.displayProfiles: named layouts for the same outputs, each a
list of nomarchy.monitors-style entries. The active profile lives in
the in-flake state (settings.displayProfile) on the night-light
pattern: nomarchy-display-profile apply <name> fires the baked hyprctl
rules instantly and writes the state with --no-switch; the next rebuild
bakes the profile's entries over nomarchy.monitors whole-by-name.
Monitor-rule composition is extracted to pure monitor-rules.nix
(renderer + 3-layer resolve, incl. a disable-guard so a stale menu
resolution pick can't resurrect a profile-disabled panel, and junk
state degrading to base config instead of an eval error) — because a
writeText/toFile state fixture is unreadable at flake-check eval time,
the new checks.display-profiles unit-tests the pure function directly.
Menu: System › Display gains a self-gated "Profiles ›" row (●/○ active
mark, Base layout to clear); commented template example + README row.
Verified: V0 (flake check, forcing the new check's asserts); V1 — a
scratch downstream through lib.mkFlake builds: the generated tool
passes bash -n with correct rules (disable/vrr/float scale), the baked
hyprland.conf shows the exact expected overlay, the menu renders, and
list/usage smoke-test clean. Remains: V3 live switching on a dock
(HARDWARE-QUEUE). Slices b (hotplug auto-switch) + c (workspace
binding) stay in item 15.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The last niceties slice: pick a pixel's color into the clipboard.
hyprpicker joins grim/slurp in systemPackages; a new nomarchy-menu
`colorpicker` mode captures its output, wl-copies it, and toasts the
hex (Esc = silent cancel). Tools › row self-gates on hyprpicker being
present; SUPER+CTRL+P added in keybinds.nix, so the Hyprland bind and
the SUPER+? cheatsheet row come from the one source. Item 13 complete
and deleted from BACKLOG.
Verified: V0 (flake check); V1 — template-home and template-system
build; rendered nomarchy-menu passes bash -n and contains the mode +
gated row; cheatsheet has the SUPER+CTRL+P row; hyprland.conf has the
bind; the system toplevel ships sw/bin/hyprpicker. Remains: V3 — real
loupe → click → wl-paste round-trip in a session (HARDWARE-QUEUE).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nomarchy-record owns the lifecycle: start prefers wl-screenrec (VAAPI
hardware encode) and falls back to wf-recorder if it dies at startup
(the no-VAAPI case), stop SIGINTs so the file finalizes cleanly, and
status feeds a self-gating Waybar ⏺ REC (alert red) whose click is
the one stop surface — signal 8 pokes make it appear/vanish
instantly. Capture gains Record region/screen (+ audio variants) and
self-swaps to '■ Stop recording' while one runs. Parity: both summer
whole-swap bars carry the same module. Recordings land in
~/Videos/Recordings with the screenshot timestamp naming (deviation
from 'next to Screenshots': an .mp4 in Pictures felt wrong).
V1: rendered bar config + CSS asserted, summer jsonc parse, recorder
dry-run state machine correct (idle/active/usage exits), both
recorders in home-path. Real capture needs a session → V3 queued
(incl. the VAAPI-vs-fallback check on the AMD box).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The things that actually break user machines, one command (and menu ›
System › Doctor): failed system/user units, disk space (real
filesystems only — fstype allowlist makes it VM/live safe, device
dedupe collapses / and /nix), theme-state.json parses + is
git-tracked, flake dirty/behind as warnings, generation age (profile
symlink mtime — store paths are epoch-1), snapper timeline when
enabled. Read-only by contract; every ✖ prints the one fixing command;
exit 1 on any problem. Ships as pkgs/nomarchy-doctor so the VM check
runs on a minimal node.
Verified: V1 package build (shellcheck-gated) + a real-hardware smoke
that correctly flagged a genuinely failed user unit on the dev box;
V2 green — checks.doctor VM test: induced failed unit flips the sheet
to exit-1 naming the unit and its fix, reset-failed returns healthy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Undo is one menu away: the new rollback flow lists the last ten Home
Manager generations (the theme/config history is already generations,
newest marked current) and activates the picked one in a terminal —
HM's supported rollback, reversible by re-picking a newer one, so no
typed-yes gate. System-level undo deliberately links out rather than
reimplementing destructive flows in rofi: the Snapshots row opens the
existing gated flow, and 'boot an older generation (how)' fires an
instruction notification pointing at docs/RECOVERY.md §3 (which now
cross-references the menu path).
V1: template-home built, generated menu bash -n clean, row rendering
and pick-parsing verified against fixture generations output. V3
exercise queued.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Audited every rofi -dmenu call site in the generated nomarchy-menu +
nomarchy-vpn. The earlier "Back everywhere" pass held up: power,
power-profile, theme, clipboard, files, audio, display (both stages),
capture, tools, system and all four VPN lists already end in ↩ Back.
Fixed: the keybinds cheatsheet — Enter on any row silently closed it;
it now ends in ↩ Back returning to the root picker (its launch point),
and Enter elsewhere stays a deliberate no-op.
Documented (rofi.nix, at the convention comment + call sites): external
modi (rofi-calc, rofi-emoji, networkmanager_dmenu, rofi-pulse-select)
cannot take an injected row — Esc is their back path; free-text
prompts (web, ask) have no list to append to.
Verified: V0; V1 (built nomarchy-menu, bash -n OK; awk audit over the
generated script shows the only back-less lists are the root picker —
by design — and the free-text prompt). V3 spot-check queued.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Source-read of the pinned networkmanager_dmenu 2.6.2: with
compact = False (what our config.ini set) the script inserts literal
empty rows as separators between the ethernet/wifi/VPN sections —
the nameless entries Bernardo saw on the Latitude. compact = True
drops the separators and uses the unpadded row format, which also
renders better in rofi's proportional font than space-aligned columns.
Known residual, documented in config comment + HARDWARE-QUEUE: a
hidden-SSID AP still renders one nameless row (sec+bars only; the
name dedup keeps exactly one, and no config option filters it). If the
V3 retest still shows it, the follow-up is a small source patch
skipping empty-name APs in create_ap_list.
Verified: V0; V1 (rendered config.ini shows compact = True). Behavior
needs Wi-Fi hardware — V3 queued on the Latitude.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follow-on to the swaync fix (a643391): the generated Waybar CSS colored
the module row and #window @subtext and dim states @muted, and the
generated rofi theme put @fg text on @surface chips — invisible where
palettes use those roles as "on-surface" (flexoki-light: subtext==base,
surface==text; gruvbox: muted/base = 1.27). flexoki-light has no
whole-swap, so it ran this CSS. Generated surfaces only — the summer
whole-swaps are hand-authored and untouched.
- waybar: secondary text → alpha(@text, 0.85); dim states (inactive
workspaces, DND bell, muted volume) → alpha(@text, 0.5).
- rofi: @dim → text+"80", @surface → text+"1a" (#RRGGBBAA fg tints).
- check-theme-contrast.py pairings/docs now cover all three generated
surfaces; status glyphs (good/warn/bad on base, worst 2.05) are
deliberately not held to a text ratio — raising them is palette
design, a human decision.
Verified: V0; V1 (checks.theme-contrast green over 21 themes;
downstream-template-home builds; rendered waybar style has zero
@subtext/@muted/@surface; rendered custom.rasi shows fg-derived
tints). Honesty: NOT visually verified — V3 queued (flexoki-light
eyeball + dark-theme regression spot-check).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
? is typed as Shift+/, so Hyprland saw mods SUPER+SHIFT with keysym
`question` and the exact-modmask match skipped the `$mod, question`
bind — the keypress fell through to the focused window (caught on the
Latitude, BACKLOG item 26; the menu itself was fine from a terminal).
- keybinds.nix: bind is now `$mod SHIFT, question`.
- rofi.nix prettyKeys collapses "SHIFT + ?" back to "?" so the
cheatsheet row keeps the documented SUPER + ? label.
- MEMORY.md gotcha: shifted keysyms need SHIFT in mods — invisible to
eval-tier tests.
Verified: V0 (flake check --no-build); V1 both surfaces (eval'd
settings.bind renders `$mod SHIFT, question, exec, nomarchy-menu
keybinds`; built nomarchy-menu's cheatsheet renders `SUPER + ?`).
V3 queued: SUPER+? on the Latitude after home-update + relogin.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SUPER+SHIFT+K → hyprctl switchxkblayout current next, rendered only
when the session layout has a comma — the same gate as the Waybar
language indicator. Bind data is a separate multiLayoutBinds list in
keybinds.nix (still one source); hyprland.nix and the rofi cheatsheet
gate on the identical condition, so bind and cheatsheet cannot drift.
`current` targets the focused keyboard, so per-device-overridden
boards (single layout) are a no-op.
Parity: hyprland/language (with the keyboard glyph) added to both
summer waybar.jsonc whole-swaps + #language in their CSS. Static JSON
cannot eval-gate, so on summer themes the module shows even with one
layout — parity-over-minimalism, deliberate. Found en route:
summer-night has an idle_inhibitor the generated bar lacks (reverse
gap) — folded into backlog item 13.
Also — agent/BACKLOG.md gains items 21-24 from Bernardo's Latitude
5410 hardware QA (waybar crash on theme switch + no respawn; nameless
network-menu rows; a no-update sys-rebuild; Back-everywhere audit),
queued ahead of everything; HARDWARE-QUEUE's snapshot re-test now
carries the "update to >= a47aa3a + relogin" precondition, since
findings 5/6 match the pre-polkit-agent behavior exactly.
Verified: V0 (flake check, both summer JSONs parse) + V1 both
directions (default single-layout build renders no bind; us,de via
extendModules renders bind AND cheatsheet row). V3 cycle check queued.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-diagnosis of the "btrfs-assistant 2.2 segfaults on launch" bug, and
the fix is not the planned nixpkgs override:
1. gdb: the crash is btrfs_util_subvolume_iterator_next() —
libbtrfsutil's UNPRIVILEGED subvolume-iteration path in btrfs-progs
6.17.1 (upstream-fixed after 6.17.1, kdave/btrfs-progs 886571653).
Symbol versions were checked and match: not ABI/link drift.
2. VM A/B on real btrfs: as root it works (exit 0); as a user it
segfaults (139). The pkexec launcher runs it as root — so the GUI
path was fine all along, except…
3. …the distro shipped NO polkit authentication agent: every pkexec
prompt in the session failed silently. That is the actual root
cause of "the GUI doesn't open".
Shipped: hyprpolkitagent (Hyprland's Qt agent — the prompt is
Stylix-themed) via exec-once in hyprland.nix; `nomarchy-menu snapshot`
prefers btrfs-assistant-launcher again, with the nomarchy-snapshots
fzf flow kept as fallback (and for SSH); checks.snapshot-gui guards
the root path + offscreen GUI event loop on a real btrfs volume so a
lock bump can't silently regress it. No btrfs-progs patch: all our
flows are root-side; the unprivileged fix arrives with a lock bump.
Verified: V0 (flake check) + V2 (snapshot-gui check, executed green) +
V1 (HM generation builds; hyprland.conf carries the agent exec-once;
generated menu passes bash -n with the launcher wiring). Remaining V3
queued: first on-hardware themed polkit prompt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
settings.autoCommit — toggled from System › Auto-commit (self-gated on
the flake being a git repo) — makes every nomarchy-theme-sync apply/set
also git-commit theme-state.json in the downstream flake, so settings
history is `git log`. Off by default.
Design: the flag is live-read by the tool on each write (nothing in Nix
consumes it → instant toggle, no rebuild, no new option); the commit is
pathspec-limited to theme-state.json so unrelated dirty files are never
swept up; it fires when the flag is on before OR after the write, so
the disable-toggle itself lands in history; same-value writes no-op
(diff against HEAD); missing git identity falls back to
Nomarchy <nomarchy@localhost>; `bg next` is deliberately excluded.
Rider fix: `get` now prints booleans JSON-style (true, not Python's
True) — un-sticking the System menu's "Auto timezone (on/off)" label,
whose `= true` comparison could never match. All shell consumers
already normalise true|True, so no other behavior change.
Verified: V0 (py_compile, nix flake check) + V1 (HM generation builds,
generated nomarchy-menu passes bash -n, wiring present in the rendered
script) + a 7-assertion sandbox-repo round-trip covering enable/set/
same-value/disable/post-disable/apply paths and the identity fallback.
Remaining: on-machine check queued in agent/HARDWARE-QUEUE.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 3 of the in-flake-state philosophy (mirroring night-light + the
keyboard-layout watcher): a System ▸ Display menu item picks an output's
resolution and persists it into the flake instead of a runtime-only tweak.
- rofi.nix: the `display` case reads `hyprctl monitors -j` (auto-skips the
chooser with one output), lists the output's advertised modes (rounded
refresh, deduped, highest-first) plus the preferred/highres/highrr
tokens, applies the pick INSTANTLY via `hyprctl keyword monitor` —
keeping the output's current position + scale, so only the resolution
changes — then persists it with `nomarchy-theme-sync set
settings.monitors.<name> <mode> --no-switch` (no rebuild).
- theme.nix: seed `settings.monitors = {}` (output-name -> "WxH@R") so a
sparse/older state file still evaluates; exposed via nomarchy.settings.
- hyprland.nix: `resolvedMonitors` overlays settings.monitors onto
nomarchy.monitors by name — a declared output keeps its other fields
with only the resolution swapped, an undeclared output (wildcard-only,
e.g. the laptop panel) becomes a fresh rule. So a later rebuild bakes
the menu pick into the generated monitor rule — the monitor twin of the
keyboard graduation. The menu pick wins over a hand-set resolution (it's
the explicit live action).
Monitor connector names are dot-free, so a direct dotted set-path is safe
(no whole-map read-modify-write like the keyboard watcher needs).
VM-verified via a headless-Hyprland nixosTest (run + removed; too heavy
for a CI gate): the graduation baked the rule into the generated
hyprland.conf, the set/get round-trip persisted, and `hyprctl keyword
monitor` actually changed the reported resolution. Only the interactive
rofi picker isn't headlessly driveable (same caveat as the keyboard watcher).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nomarchy.services.tailscale now makes the login user the Tailscale
operator (services.tailscale.extraSetFlags = ["--operator=<username>"],
mkDefault), so `tailscale up/down/set` — and the System → VPN menu's
Tailscale controls — run without sudo. The user is already in wheel, so
the operator grant is no real new privilege, just skips the prompt.
nomarchy-vpn's Tailscale actions are now operator-aware: a ts_priv
helper runs the subcommand inline and only falls back to a sudo terminal
if the operator grant is absent (downstream dropped it). Connect brings
an authed-but-down node up inline; a node that still needs interactive
login goes to a terminal so the auth URL is visible.
Option description + template comment updated; ROADMAP follow-up closed.
System + home builds green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dedicated System → VPN flow (nomarchy-vpn) past the wifi picker:
- NetworkManager VPN/WireGuard connections shown ● active / ○ inactive,
toggled up/down on select (networkmanager-group users need no sudo).
- Import a *.conf / *.ovpn via the fd picker → nmcli connection import,
type chosen by extension. networkmanager-openvpn ships system-side
(networking.networkmanager.plugins, mkDefault) so the openvpn type is
available; WireGuard is native to NetworkManager.
- Tailscale block, self-gated on the CLI (= nomarchy.services.tailscale):
read-only status + up/down + exit-node; privileged actions go through a
sudo terminal (the snapshot pattern), no operator wiring.
Waybar custom/vpn shield (nomarchy-vpn-status): self-hides unless a NM
tunnel or Tailscale is up; @good tone; click opens the submenu. Wired
into the generated bar and both summer whole-swaps. Decided import-first;
creation deferred to nm-connection-editor. ROADMAP marked shipped.
Eval + home/system builds green; both scripts pass bash -n. Pending an
on-machine check (live nmcli import/connect + Tailscale paths).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nomarchy.system.autoTimezone (off by default): geoclue + automatic-
timezoned drive /etc/localtime from your location, so travelling to
another zone updates the Waybar clock on its own.
Menu-driven, in-flake state (the night-light / keyboard philosophy):
the flag is settings.autoTimezone in theme-state.json (git-tracked); a
System-menu entry toggles it. Because it's a SYSTEM service — not a user
unit it can start/stop instantly like night-light — the toggle
(nomarchy-autotimezone, run in a terminal) writes the flag --no-switch
then drives `sudo nixos-rebuild` (bakes the service + the time.timeZone
override) plus `home-manager switch` (the Waybar-refresh watcher); both
sides read the one flag.
- modules/nixos/timezone.nix: parses settings.autoTimezone from
nomarchy.system.stateFile (like Plymouth), gates geoclue2 +
automatic-timezoned, and mkForce-nulls time.timeZone — automatic-
timezoned sets it null at normal priority, which collides with the
installer's static value (a hard eval error), so force wins and
reverts cleanly when disabled. Ships the nomarchy-autotimezone toggle.
- modules/home/timezone.nix: a tiny user service watching timedate1's
change signal that reloads Waybar (SIGUSR2) on a real zone change, so
the clock follows live (Waybar captures the zone at construction).
Gated on the same flag via nomarchy.settings.autoTimezone.
- rofi menu: "Auto timezone (on/off)" in the System submenu.
- option + template example + ROADMAP entry; theme.nix seeds the flag.
Eval-verified on (geoclue+daemon on, tz forced null over a static
installer value, watcher present) and off (static tz intact, nothing
extra); home + system both build green. Needs an on-hardware check
(geoclue detection + the SIGUSR2 clock refresh aren't CI-testable).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
A `nomarchy-menu printers` entry opens system-config-printer (the CUPS
admin GUI), mirroring the bluetooth/blueman pattern: the printing service
ships the package via environment.systemPackages and the menu execs it,
self-gated on the system-config-printer binary so the entry appears only
when nomarchy.services.printing is on. No direct keybind (rare one-off).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
networkmanager_dmenu writes a default config on first run, so an
unmanaged file can already sit at ~/.config/networkmanager-dmenu/
config.ini and abort `home-manager switch` with a clobber error. We
fully generate this file, so set force = true to overwrite it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- network: replace the nmtui-in-a-terminal flow with networkmanager_dmenu
(native rofi wifi/VPN picker), configured via xdg config.ini to drive
rofi with rofi_highlight; editing a connection drops to nmtui.
- audio: new System -> Audio sink/source switcher via rofi-pulse-select,
self-gated on the PipeWire/pulse socket.
- search: case-insensitive fuzzy matching with fzf sorting across every
menu module and the launcher (so "system" finds "System").
Secrets module (rbw/pass) deferred — no Bitwarden/pass setup here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the plain-text `nomarchy-menu theme` list with a grid of real
desktop previews. The grid, the Name→slug map and the active `✓` mark
are generated at eval time from the preset JSONs (`readDir` + `fromJSON`
in rofi.nix); "active" is just `t.slug`, since every switch rebuilds the
menu. Grouped dark-first then light in one scrollable grid (the previews
make the mode obvious — no light/dark submenu split), `flow: horizontal`
so Down scrolls row-by-row.
Previews are committed as themes/<slug>/preview.png at 480×270 (~2.4 MB
for all 21). rofi's element-icon `size` is single-value → a *square*
cell that *contains* the icon, so a 16:9 preview would letterbox; a
build-time imagemagick step centre-crops each to a square so it fills
the cell edge-to-edge (source stays 16:9, crop is reversible). The
window width is derived from `themeGridIconW` so a column is exactly the
icon side — no slack margins. Themes without a preview degrade to a
plain-name row.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two menu improvements.
Back everywhere: every list menu now ends with a "↩ Back" entry that
returns one level up — power, theme, power-profile, clipboard, files,
capture, not just Tools/System — so you no longer have to Esc out and
reopen to back up. A `back` helper + a shared `BACK` label keep it uniform;
matched exactly so it can't collide with clipboard/filename content. Esc
still quits instantly.
Snapshot fallback: btrfs-assistant 2.2 segfaults on 26.05 (libbtrfsutil
ABI), so `nomarchy-menu snapshot` now launches nomarchy-snapshots instead —
a keyboard-driven snapper browser/restore. snapper is root-only here and
rofi can't run as root under Wayland, so it runs in a terminal via sudo
(one password prompt) and uses fzf to pick: browse/diff (read-only),
restore files (undochange), or roll back (root config only), each behind a
typed-`yes` confirmation — safer than a one-click rofi rollback.
btrfs-assistant stays installed for when nixpkgs fixes it.
Verified: flake check clean; home generation builds and the menu passes
bash -n with all Back + snapshot wiring; nomarchy-snapshots passes
shellcheck (writeShellApplication) in a snapper-enabled system build.
Pending an on-machine check of the restore/rollback paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a runtime toggle for the scheduled blue-light filter, the remaining
optional item on the night-light roadmap entry.
nomarchy-nightlight (nightlight.nix, always on PATH) starts/stops the
hyprsunset *service* rather than poking its runtime gamma: `systemctl
--user is-active` is the single source of truth, the screen restores to
true colours when stopped, and it lets you force the filter off for
colour-sensitive work (even at night) and back on without touching config.
It self-gates — `status` prints nothing when the unit is absent — so the
indicator hides itself when night-light isn't enabled.
- Menu: `nomarchy-menu nightlight` toggles it; a self-gated row in the
System submenu (shown only when the hyprsunset unit exists).
- Waybar: a self-gating `custom/nightlight` indicator (moon = on, sun =
off), in the generated bar and both summer whole-swap themes for parity.
Verified: home generation builds, generated waybar config carries
custom/nightlight, the menu has the dispatcher case + System row, both
scripts pass bash -n, and status self-gates to empty with no unit. Pending
an on-machine check that stopping hyprsunset cleanly restores the gamma.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reworked the menu look, palette-driven so it lands on every theme:
- Real Papirus icons in place of Nerd Font glyphs: a row() helper emits
rofi's per-row icon protocol, and the five static menus (root, Tools,
System, Power, Capture) map each entry to a freedesktop icon name
(verified present in Papirus). Entry text is now clean ("Apps"), the
case-matching is unchanged.
- Larger icons via a new t.ui.iconSize knob (36px, generated theme); the
summer whole-swaps keep their 40px identity.
- Rounded, roomy rows with subtle zebra striping: alternate rows lift to
@surface (a faint bg0 wash on the summer cream/slate windows), the
selected row to @accent — using rofi's normal/alternate/selected row
parities.
- Content-fit windows: fixed-num-lines=false so a 6-entry menu stops
reserving 8 rows of empty space (the launcher still fills + scrolls).
Dropped the fixed height:50% from the summer .rasi so they size to
content too — which also unhid their clipped Power/Keybindings rows.
- Title-cased the menu prompts.
Verified by building the HM config and rendering the generated theme plus
both summer whole-swaps headlessly (sway + grim): icons resolve, zebra
shows, every menu hugs its rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The root picker had grown to 16 flat entries. Collapse it to six —
Apps · Theme · Tools › · System › · Power · Keybindings — with Tools
(Calculator, Clipboard, Emoji, Files, Web, Capture, Ask) and System
(Network, Bluetooth, DND, plus the self-gated Snapshots / Power-profile)
as submenus the dispatcher routes via `nomarchy-menu tools|system`. Each
submenu ends in a ← Back entry that re-opens the root. The direct
SUPER+CTRL+<mnemonic> binds still reach the leaves straight (bypassing
the menu), and the gated entries still hide when unavailable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface snapper snapshots from the desktop: `nomarchy-menu snapshot` (in the
SUPER+M picker, self-gated) launches btrfs-assistant, the polkit-aware
browse/diff/restore/rollback UI -- safer than a fat-fingerable rofi rollback
for a destructive root-only op. Shipped system-side gated on
nomarchy.system.snapper.
Also snapshot /home by default (hourly 5 / daily 7 / weekly 4) when it's its
own BTRFS subvolume -- the installer only set up snapper for /. A boot
oneshot creates the required /home/.snapshots subvolume if missing, so
already-installed machines get it too (no disko change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a Do-Not-Disturb story on top of swaync:
- Waybar `custom/notification` module via `swaync-client -swb` — streams
the notification count + DND state (no polling). Left-click toggles the
panel, right-click toggles DND; bell-off glyph + muted color when DND is
on, accent when notifications are waiting.
- `nomarchy-menu dnd` toggle in the root picker and bound to SUPER+CTRL+D
(so it shows in the SUPER+? cheatsheet). Turning DND off confirms with a
toast; turning it on is silent — notifications are suppressed and the
bell-off icon is the cue.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The rofi-file-browser-extended navigate-a-tree model felt worse than flat
fuzzy-find for a quick launcher (yazi already covers real browsing), so
`files` goes back to `fd` → `rofi -dmenu` → xdg-open. calc (rofi-calc)
and emoji (rofi-emoji) stay. Re-adds `fd` to the module's packages and
notes the dropped plugin in the roadmap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the hand-rolled calc/files modules with purpose-built rofi modi
(via programs.rofi.plugins) and add an emoji picker — all themed through
the same .rasi as every other modi:
- calc -> rofi-calc: live results as you type via libqalculate directly,
which also dodges the qalc CLI's "15% of 200" -> rem(15, 1 B) misparse.
Enter copies; the menu persists to chain calculations. Drops the
libqalculate (qalc CLI) dependency.
- files -> rofi-file-browser-extended: live directory navigation +
xdg-open, replacing the flat 50k-cap `fd` dump of $HOME. yazi (SUPER+E)
stays the power browser; `fd` already ships via shell.nix.
- emoji -> rofi-emoji (new module): glyph picker, copies through the
plugin's Wayland clipboard adapter. Bound to SUPER+CTRL+E in
keybinds.nix (so it lands in the SUPER+? cheatsheet too).
rofi 2.0 rebuilds cleanly with all three plugin .so (ABI-matched, same
nixpkgs). README roadmap updated: calc rework marked shipped, with a
follow-up item for the script-based plugins (network/secrets/audio).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a `nomarchy.system.power.*` surface (modules/nixos/power.nix),
replacing the bare `services.upower` (Waybar reporting only) baseline:
- power-profiles-daemon ships by default; switched through polkit (no
root prompt). TLP is the opt-in `backend = "tlp"` for deeper battery
tuning — the two are mutually exclusive (asserted).
- thermald behind `power.thermal.enable` (Intel-only).
- `power.batteryChargeLimit` (e.g. 80): a backend-independent sysfs
oneshot writes charge_control_end_threshold; gated on `power.laptop`.
Switcher + indicator live home-side and self-gate on a battery being
present + powerprofilesctl running, so they hide on desktops and under
TLP — no system->home wiring (like the Waybar battery widget):
- rofi.nix: `nomarchy-menu power-profile` module + a picker entry shown
only on laptops with PPD.
- waybar.nix: first `custom/*` module — click cycles the profile.
Installer writes `power.laptop = true` (battery probe) and
`power.thermal.enable` (GenuineIntel) into the generated system.nix, and
scaffolds the charge limit commented-out.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The web module now opens google.com/search instead of DuckDuckGo. Also log
a roadmap item to rework calc: qalc -t misparses common phrasings (15% of
200 -> rem(15, 1 B)) and the result only shows in the next menu's -mesg
line -- move toward live results as you type.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build out the rofi menu system roadmap item:
- New nomarchy-menu modules: network (nmtui), bluetooth (blueman-manager),
capture (grim/slurp submenu → clipboard/file), ask (free-text → claude
CLI in a terminal), and keybinds (the SUPER+? cheatsheet).
- modules/home/keybinds.nix is now the single source of truth for both the
Hyprland bind strings and the cheatsheet, so the two can't drift —
hyprland.nix maps it into `bind`, rofi.nix renders the padded two-column
sheet (generated/mouse binds live in its `extra` rows).
- New binds: SUPER+Space (quick launch), SUPER+M (main menu), SUPER+?
(cheatsheet). SUPER+D stays rofi -show drun.
- Ask Claude pulls claude-code fresh from npm via `npx
@anthropic-ai/claude-code@latest` (the nixpkgs package lags model
releases); nodejs is bundled for npx.
nix flake check passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ships papirus-icon-theme and resolves an icon-theme name once in
theme.nix → nomarchy.theme.iconTheme: the JSON's optional `icons` field,
else Papirus-Dark/Light picked by `mode`. That single value feeds:
- Stylix `stylix.icons` → gtk.iconTheme (Thunar and GTK apps)
- rofi `show-icons` + `icon-theme` (the launcher now shows app icons)
A preset or theme-state.json can set `icons` to any theme in the icon
package to override per theme.
Verified: flake check green; iconTheme resolves Papirus-Dark for
tokyo-night and Papirus-Light for summer-day; gtk.enable true with
gtk.iconTheme.name set; papirus in home.packages and the live ISO
closure; rofi config.rasi carries icon-theme + show-icons:true. The
rendered icons need a real session to see.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
rofi 2.0 landed in nixpkgs 26.05 with native Wayland (mainline upstreamed
the lbonn rofi-wayland fork — `wayland enabled`), so the original reason
to prefer fuzzel (rofi being X11-only / needing a community fork) is gone.
rofi's `.rasi` is far more expressive than fuzzel's flat INI, which lets
the launcher be a proper themed hero surface and revives the legacy
per-theme launcher designs.
- modules/home/rofi.nix replaces fuzzel.nix: per-element theme generated
from theme-state.json (accent border, highlighted selection, rounded
inputbar) via lib.formats.rasi.mkLiteral; themes/<slug>/rofi.rasi
whole-swap; the nomarchy-menu dispatcher re-pointed to `rofi -dmenu`
(power/theme/clipboard/calc with -mesg/files/web + root picker).
- SUPER+D is now `rofi -show drun`; the menu binds are unchanged.
- nomarchy.fuzzel.enable → nomarchy.rofi.enable; fuzzel dropped from
home.packages. Pango handles nf-glyph fallback (no explicit font list
needed as fuzzel's fcft did). show-icons off until an icon theme ships.
Verified: flake check green, HM generation builds, the generated
theme.rasi parses under `rofi -dump-theme`, Hyprland config verifies.
Not verified: rofi's on-screen rendering needs a real session.
Docs: README menu/override/roadmap sections and the module tree updated;
the per-theme launcher roadmap item now points at porting the legacy
rofi.rasi designs (the whole-swap mechanism is in place).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>