He supplied a reference `power-menu` rofi script (battery charge + power
profile rows, icon protocol, notify-send). Un-gates the design hold on
36(b): records the shape + the Nomarchy adaptation (reuse the existing
batterylimit/power-profile writers instead of raw TLP; wire the Waybar
battery module on-click). One [human] call left: replace the separate
System rows or keep both surfaces.
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>
Investigating the right-click mixer showed the config has zero
windowrules today, so the float rule needs the infrastructure item 41 is
meant to design list-first. Recorded the gate + the mixer recommendation
(pwvucontrol, app-id com.saivert.pwvucontrol) so the research is not lost.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Found every authored .rasi deliberately hides the scrollbar (width:0), so
the overflow hint needs per-theme tuning across ~10 files with a V3-only
payoff — recorded so it is picked up in a themed session, not blind.
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>
Add SHIFT+Print (region → file) and CTRL+Print (screen → file) next to
the existing bare Print (region → clipboard). Each saves a timestamped
PNG under ~/Pictures/Screenshots and toasts the path — the same grim/slurp
plumbing and directory the Capture menu's "→ file" rows already use.
Single-source keybinds.nix, so the SUPER+? cheatsheet lists both new rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two rofi fixes:
- Search (item 31): rofi 2.0.0's fzf sorter mis-ranks fuzzy matches —
typing "steam" left ghostty selected instead of Steam. Bernardo's live
A/B confirmed `-sorting-method normal` and `-matching normal` both fix
it while `-no-sort` does not. Switch sorting-method fzf → normal, keep
matching=fuzzy + case-sensitive=false.
- Power profile menu (item 39): list profiles with per-profile icons from
Papirus' colored battery-profile-{performance,balanced,powersave} family
(the -symbolic variants are #444 grey, invisible on dark). Row text
stays the bare profile name so `powerprofilesctl set` is unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The mode picker's Back did `exec "$0" display`. On a single-monitor
machine with no display profiles the output chooser is skipped, so
re-running `display` dropped straight back into the same mode picker — an
apparent no-op. Track modeBack (display when the chooser was shown, else
system) and dispatch Back to it, so single-monitor Back leaves Display.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Bugs: 31 rofi search broken under rofi 2.0.0 (lock bump 8fded63 pulled a
major version; case-insensitive fuzzy is configured + honoured yet
matching behaves case-sensitively and Enter launches row 0), 32 SUPER+?
still not opening, 34 Back row shows double arrows, 40 Display menu Back
doesn't return. Plus 36 battery/power quick menu (move the threshold
toggle into the rofi System submenu + a battery-icon power menu, mockup
pending), 39 power-profile menu icons. Features (NEXT tail): 33 rofi
page-overflow scrollbar, 35 right-click volume → mixer, 37 default
media players + mime, 38 capture-to-file binds, 41 floating-window audit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Battery Limit" entry was a raw number prompt; make it a proper
toggle picker — Off / 80% (recommended) / 90% / 60% / Custom… — writing
settings.power.batteryChargeLimit. Kept in the control-center, where the
baked rebuild-valued System Toggles live (the rofi nomarchy-menu is the
instant surface; the CC holds config values), and reachable from the menu
via System › Control Center. Base machinery (the sysfs oneshot + AC-replug
udev in power.nix) is unchanged, so it lands on the next sys-rebuild like
its siblings.
Instant-effect (no rebuild) is refiled in BACKLOG § PROPOSED as
[blocked:hw]: it needs a udev-writable threshold node, confirmable only on
a laptop with the control.
Verified: V0 (bash -n; nix flake check --no-build, green) + V1
(nomarchy-control-center package builds — the writeShellApplication
shellcheck gate passes). Session spot-check queued in HARDWARE-QUEUE.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Raised by Bernardo. The charge-limit base already ships (batteryChargeLimit
option + systemd sysfs writer + udev re-trigger + control-center entry +
VM check); the gap is UX — it's a type-a-number system option needing a
rebuild. Filed under PROPOSED as a one-click conservation-mode toggle
(~80% ↔ 100%), ideally instant-effect, referencing the existing mechanism
so the base isn't re-implemented.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On Hyprland the `top` layer renders above every window, including a real
fullscreen surface — so a browser video gone fullscreen sat *under* the
bar (BACKLOG item 30). Move the bar to `layer: bottom` in the generated
waybar.nix and all four whole-swap jsoncs (parity rule); the exclusive
zone still reserves its space in normal tiling, but a fullscreen window
now covers it. Rationale comment kept in waybar.nix; data jsoncs left
comment-free (they carry none).
Verified: V0 (nix flake check --no-build, green) + V1
(homeConfigurations.nomarchy.activationPackage builds; built
.config/waybar/config shows "layer": "bottom"). V3 queued in
HARDWARE-QUEUE (fullscreen covers bar + returns on exit; floating-overlap
trade-off).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
29 (docking/external-monitor UX): unplug→restore the internal panel,
plug→extend by default, easy HDMI/DP audio routing, and clearer
workspace↔monitor behavior. Raised by Bernardo; [human]/[blocked:hw].
30 (waybar overlaps fullscreen video): a fullscreen YouTube video is
drawn under the bar's overlay layer; hide the bar on true fullscreen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A display profile can now pin workspaces to outputs:
docked = {
monitors = [ … ];
workspaces = { "1" = "DP-3"; "9" = "eDP-1"; };
};
The bare list-of-monitors shape still works (either-type; hyprland.nix
normalizes — coercedTo refuses list-of-submodule sources). Pins bake as
Hyprland `workspace` rules from the active profile and apply instantly
on `nomarchy-display-profile apply`: keyword sets the session rule,
moveworkspacetomonitor moves open workspaces over. Pins from a
previously applied profile linger in-session until reload/rebuild
(hyprctl can only add) — inert if the output is absent, noted in the
applier.
V1: flake check green (checks.display-profiles now asserts
workspaceRule) + scratch-downstream eval: baked settings carry both
rule sets, the generated applier has the keyword+dispatch pairs
(bash -n), match/list correct with mixed shapes. V3 queued in
HARDWARE-QUEUE (rides the dock test). Item 15 complete — deleted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
catppuccin, lumon, nord and retro-82 carried 2-14 line waybar.css
stubs from the rewrite import (f211ef0). A theme waybar.css replaces
the entire generated stylesheet, so those four shipped a raw default
Waybar: no workspace pill, cramped flush-edge right cluster, none of
the generated polish. Deleting the stubs falls them back to the
generated bar in their own palette; their full ~110-line rofi.rasi
designs are untouched. Authored whole-swap bars remain only
summer-day/night.
V2: lumon captured before/after — stub bar was default-styled,
re-capture renders the generated styling. flake check green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
theme-shot guests had no ~/.nomarchy, so the session's exec-once
`nomarchy-theme-sync wallpaper` died on missing state and captures
showed a bare base-color backdrop. Seed theme-state.json via tmpfiles
before greetd starts the session — the real exec-once path now paints.
(A post-boot testScript seed is too late for exec-once, and a manual
re-run fails without WAYLAND_DISPLAY: awww's client socket is
display-named.)
V2: summer-night capture renders wallpaper + inverted cream bar +
menu + placeholder dim, all coherent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hovering the clock now leads with the zone — "{:%Z (UTC%z)}" above the
calendar — on the generated bar and both summer whole-swaps (parity).
Under auto-timezone the existing tz-watch SIGUSR2 reload keeps it
showing the currently detected zone. %Z is the abbreviation (BST), not
the IANA name — strftime has no code for that, and it satisfies the
"(optional)" nicety.
Verified: V0; V1 — the rendered generated config carries the new
tooltip-format and both summer jsonc parse with it. Hover render
queued for hardware.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nomarchy-doctor-status: silent while nomarchy-doctor exits 0 (or is
absent), so the module only exists when something is actually broken —
then a red warning glyph with the first ✖ lines jq-escaped into the
tooltip; clicking opens the full sheet (nomarchy-menu doctor).
Interval 300s: the health check is real work (unit/disk/git sweeps),
and this is a tripwire, not a monitor. Added to the generated bar
(status group, after battery) and to BOTH summer whole-swap bars
(module list + jsonc block + CSS membership + @red rule) per the
parity rule.
Verified: V0; V1 — template-home builds with the module in config +
CSS, the status script passes bash -n, both summer jsonc still parse;
functional — the self-gate exits silently, and a shimmed failing
doctor yields jq-valid JSON with class "bad" and the ✖ lines in the
tooltip. Live render queued for hardware.
Co-Authored-By: Claude Fable 5 <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>
Both summer whole-swap rasi rendered prompt and placeholder in the
same bg0 on the green inputbar, reading as one run-on phrase — the
placeholder is now bg0 at 60% alpha, matching the generated theme's
hierarchy.
Recorded with it: the catppuccin-latte 1080p capture shows the
generated menu fully coherent on light themes (the audit's "dark
terminal on latte" was a stale preview), and a dedicated VM probe
confirms misc:background_color is applied (hyprctl getoption =
0xFFEFF1F5) — the dark backdrop in captures is awww's empty-state
layer over it, a wallpaper-less-VM artifact, not a defect.
Verified: rasi edits grep-verified (visual rides the next summer
capture / hardware); latte capture + probe reviewed. Item 28 is now
gated on the icon-weight [human] call and the queued hardware
eyeballs.
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>
Two polish items from the 1080p review. The right cluster gains a
wider breath (margin-left 14px) before each functional group — media
(pulseaudio/cpu/memory), toggles (powerprofile/nightlight/language),
status (battery/updates/notification); a self-hiding group head
degrades to the uniform spacing. Dim states (inactive workspaces, DND
bell, muted volume) move from alpha(@text, 0.5) tints to the
palette's @muted role — safe now that 28b floor-gates muted/base >=
2.0 in every theme; the CSS comment that warned against @muted
documented the pre-28b palettes and now states the new invariant.
Whole-swap bars untouched (authored identity).
Verified: V0; V1 (rendered CSS carries the rule + 4 @muted uses);
visual — tokyo-night 1080p re-capture shows the themed backdrop
(iteration #35's fix, navy not black), the group gap, and legible
muted workspaces.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The compositor backdrop behind a missing or still-loading wallpaper
was hard black; on light themes that flash reads as a glitch (caught
by the 1080p capture harness on summer-day). Now the theme's base
color, so every wallpaper-less moment stays in-palette.
Also from the summer-day capture review: the authored whole-swap menu
+ island bar survive the 28b retunes as one coherent identity (the
slate menu deliberately mirrors the bar strip — an earlier "polarity"
concern retracted); nit filed for the undimmed placeholder in the two
summer rasi. Punch list updated in item 28.
Verified: V0; V1 — template-home rebuilt, hyprland.conf renders
background_color=rgb(1a1b26). Visual confirmation rides the next
capture run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tools/theme-shot.nix: boots the full themed desktop headlessly
(software-GL Hyprland, THEME env picks the slug) at 1920x1080 and
QMP-dumps desktop.png + menu.png (rofi root open) — the review
artifact for slice (c). Two fixes over the 2026-06-19 spike:
home-manager.useGlobalPkgs (the HM submodule otherwise misses the
overlay) and the menu shot via hyprctl dispatch exec. Maintainer tool,
deliberately not a checks.* gate. Known gap in the header: the
wallpaper needs ~/.nomarchy seeded in the guest.
First full-res review (tokyo-night) added to the item-28 punch list:
rofi root menu is ~760px of mostly empty row space (candidate:
right-aligned SUPER+CTRL hints from keybinds.nix), full-color Papirus
menu icons vs the all-monochrome bar glyphs is a [human] identity
call, bar right-cluster group margins confirmed at full res, and the
bar's alpha(text,.5) dim states should become the @muted role now
that 28b floors guarantee its legibility. Whole-swap bar module
parity verified textually — no drift.
Verified: harness end-to-end green (screenshots read back); evals
from tools/ via the relative flake path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
18 hex retunes, hue-preserving OKLCH minimal raises except where the
upstream palette defines a canonical value: muted lifted to >= 2.0 on
lumon/white/retro-82/everforest (solved), gruvbox (bg4 #7c6f64), nord
(#616e88, the community comment-brightening), catppuccin-latte
(overlay1 #8c8fa1); text-on-surface >= 4.5 via surface nudges
(ristretto darker, latte lighter); accentAlt >= 3.0 (latte, summer-day
darkened); warn >= 2.5 — the bar's battery tint — darkened on the
light bases (latte, rose-pine dawn, summer-day) and flexoki-light's
four status colors moved to their canonical light-mode 600 series.
check-theme-contrast.py now gates all four adopted floors (7 pairings
x 21 themes, green). good/bad stay audit-only — identity themes sit at
2.0-2.3 there by design, so no exemption mechanism was needed. The
design audit's contrast findings drop 20 -> 1 (miasma's earthy `bad`,
kept as identity). Item 28 slice (b) complete.
Verified: V0, checks.theme-contrast green, flake check green,
re-audit. Rendering is V3 -> HARDWARE-QUEUE eyeball entries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit's P1s, fixed at the palette level (items 25/27 previously
patched the consumers around these):
- summer-day: surface was the TEXT color (slate #5c6a72 on cream) and
subtext was the BASE (ratio 1.0, invisible) — now a raised cream
surface #efe7d0 and a slate-teal subtext #6e828a in the theme's
family.
- flexoki-light: surface/overlay/muted were all the text black and
subtext the paper — retuned to canonical upstream Flexoki tones
(ui #E6E4D9, #B7B5AC, tx-2 #6F6E69, base-500 #878580).
- miasma: pure-black surface under a #222 base → raised #2e2e2e.
- kanagawa: surface was sumiInk0 (darker than the bg) → upstream's
raised sumiInk4 #2a2a37.
check-theme-contrast.py gains the adopted rule: subtext on base >= 3.0
(all 21 themes pass), so the invisible-secondary-text class cannot
ship again. Model note in the backlog: `overlay` is a mid-tone role
(catppuccin semantics), not a third bg tier.
Verified: V0 + checks.theme-contrast green (21x3); re-audit shows all
four P1 findings gone; a scratch downstream with summer-day active
builds and the new surface hex lands in the rendered ghostty config.
Remains: V3 four-theme eyeball (HARDWARE-QUEUE) and 28b's P2 floors +
exemption list.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tools/audit-theme-design.py (report-only, maintainer-run): for every
themes/*.json it checks OKLCH lightness architecture (raised bg stack,
fg hierarchy), WCAG contrast beyond the guarded pairs, status-color
hue families, accent harmony, protanopia/deuteranopia
distinguishability (Machado matrices), and ANSI slot semantics the
greeter/tty now rely on. Ran it over all 21 palettes + a visual pass
over representative previews; findings curated identity-aware (lumon/
vantablack/white/hackerman exemptions are identity, not defects) into
the P1-P3 punch list now recorded in BACKLOG item 28: two palettes
with literally invisible subtext (summer-day, flexoki-light — the
item-25/27 trap at its source), two inverted bg stacks (miasma,
kanagawa), muted/warn floors, a systemic "status is never color-only"
rule, the light-theme ANSI convention decision, and bar grouping
rhythm notes.
Verified: V0 (py_compile; the tool is report-only, deliberately not a
checks.* gate until slice b adopts rules + exemptions).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Human-requested: a thorough visual review of the whole themed surface
(Waybar incl. whole-swaps, rofi, swaync, hyprlock, terminals, splash,
greeter) with "stunning" as the bar. Grounded in design theory rather
than taste: color-harmony models + perceptual (OKLCH) lightness
ordering of the palette roles, color-blind-safe status colors, an
extended contrast checker encoding the adopted rules, a consistent
type scale + spacing grid, and explicit visual hierarchy. Image-expert
models are available for designed artwork — slices that need graphics
stop and ask instead of shipping programmer-art. Sliced audit-first.
Placed at the head of NEXT for Bernardo to reorder.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New modules/nixos/greeter.nix owns the greetd block (moved out of
default.nix) plus the theming, the Plymouth model — baked at SYSTEM
rebuild from nomarchy.system.stateFile. Two parts, because tuigreet
draws on the VT with the 16 ANSI slots: console.colors becomes the
theme's ansi[] (vt.default_* kernel params — which also themes raw
ttys and the LUKS passphrase prompt), and tuigreet gets a --theme of
NAMED slots (its parser is ratatui Color::from_str, checked in source;
the names map to the standard indexes the retinted palette now
serves). A sparse state without ansi skips theming cleanly.
Verified: V0; V1 — the template-system toplevel bakes the full --theme
spec into greetd.toml and all three vt.default_{red,grn,blu} palettes
into kernel-params. A checks.greeter VM test was attempted and dropped
after bisecting: tuigreet dies under runNixOSTest even without the
flag (harness limit; nixpkgs's own greetd test uses agreety) —
recorded in agent/MEMORY.md. Remains: V3 rendering +
theme-follows-rebuild on hardware (HARDWARE-QUEUE).
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 item required a written plan before coding. Option shape reuses the
existing monitorType submodule (attrsOf (listOf monitorType)); active
profile is in-flake state (settings.displayProfile) on the night-light
instant+bake pattern; whole-entry-by-name rebuild overlay with a
disable-guard so a stale menu resolution pick can't resurrect a
disabled panel; self-gated System > Display profile row; sliced a/b/c
with hotplug auto-switch and workspace binding deferred behind the
core picker. kanshi remains a non-goal.
Verified: V0 (docs-only change).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.gitea/workflows/bump.yml: Mondays 05:17 UTC (+ workflow_dispatch for
security bumps), nix flake update — within the pinned release branches
by construction, never a release jump — gated by nix flake check
--no-build, then a pathspec-limited flake.lock commit pushed to main
with the update's change list as the body. That push triggers check.yml
as a second net. Red gate or push race = a visibly failed run, nothing
lands. v1 promotion stays human. py_compile/bash -n deliberately not in
the gate: they don't read the lock. Container recipe inherited from
check.yml (nixbld, sandbox=false, pinned Nix 2.31.5).
Verified: V0 (yq parse, bash -n per run block); the update/commit/push
logic simulated end-to-end in a scratch clone with a real nix flake
update and a bare remote (changed + unchanged branches, bystander file
stays uncommitted, push lands); today's bumped lock evals green.
Remains: the first real run (cron pickup + Actions-token push) is not
confirmable from a session — BACKLOG item 14 slimmed to that watch.
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>
The bar colored the battery at 25/10% but nothing notified. New
shellcheck-gated watcher (pkgs/nomarchy-battery-notify, overlay) polls
the same sysfs the bar reads — type=Battery, scope!=Device — and fires
one toast per downward crossing (normal at 25%, critical at 10%; swaync
keeps critical up until dismissed), re-armed by charging. Self-gates on
battery presence (the powerprofile pattern), so desktops get a silent
no-op. HM user unit behind nomarchy.batteryNotify.enable (default on)
with libnotify on the unit PATH; notify-send resolves from PATH so the
VM check can shim it. poweralertd rejected: its UPower thresholds
(20/5) wouldn't match the bar, and it toasts every plug/unplug.
Verified: V0 (flake check, bash -n); V1 (template-home builds, unit in
the generation); V2 GREEN (new checks.battery-notify, test_power fake
battery: self-gate, silence at 80%, one toast per crossing, critical
urgency, charging re-arms). Remains: V3 — real swaync toast on a
draining laptop (HARDWARE-QUEUE).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summer-night's whole-swap bar had an idle_inhibitor the generated bar
lacked (reverse parity gap from the 2026-07-04 sweep). The generated
bar now carries it — click → , warm tint while active, tooltips
saying what it holds — blocking hypridle's lock/display-off/suspend
for videos and presentations; state deliberately resets with the bar.
summer-day mirrors summer-night's module + CSS, and the keybinds
cheatsheet mentions the bar click (via keybinds.extra).
V1: rendered config has the module with correct glyphs, CSS rules
present, cheatsheet row renders. Inhibit behaviour needs a session →
V3 queued.
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>
'Never has to master Nix' includes the error messages. The tool grows
validate_state (appearance schema = hard errors; settings.* and
unknown keys = warnings so menu writers and newer schemas keep
working), a read-only `validate` subcommand, and validation BEFORE
every write — an invalid set/apply is refused with the on-disk file
untouched. JSON syntax errors now say line, column, and fix. theme.nix
enforces the same schema at eval time (on the post-defaults state, so
sparse/older files still evaluate) and throws a message naming each
field, its got-value, and the fix paths — the hand-edit escape hatch
no longer ends in a Nix stack trace. Doctor's JSON fix-text points at
validate.
V1: new checks.theme-sync-validate fixture corpus green (good state,
trailing comma, bad hex, bad type, unknown key, refused set with
byte-identical file); negative mkFlake eval of a broken scratch
downstream shows the friendly throw; good-path template-home builds.
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>