Compare commits

...

50 Commits

Author SHA1 Message Date
Bernardo Magri
83218a01f3 docs: add "Working on Nomarchy" orientation guide
Some checks failed
Check / eval-and-lint (push) Failing after 6m21s
A short, task-indexed guide for navigating the repo: the core/features/themes
mental model, the two app-config namespaces, the four script locations (incl.
themes/engine/scripts/), the build/test loop, how theming works, and a
Walker+elephant deep-dive covering the dmenu vs custom-provider menu paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:32:24 +01:00
Bernardo Magri
9fe6d9e405 fix(walker): don't spawn a duplicate elephant alongside the service
nomarchy-launch-walker guarded its manual elephant start with
`pgrep -x elephant`, which never matches the Home Manager service's actual
process name `.elephant-wrapped` (Linux truncates comm to 15 chars). So every
menu invocation spawned a second elephant that races elephant.service for the
socket — a latent source of menus intermittently coming up empty.

Gate on `systemctl --user is-active elephant.service` first, with a working
`pgrep -f elephant-wrapped` fallback for non-service setups; do the same for
walker.service. Verified in a headless system.build.vm: opening the theme menu
now keeps the elephant process count at 1 (was 2) and the menu still renders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:32:12 +01:00
Bernardo Magri
8a302e8771 chore: drop omarchy pkg-* compatibility shims
Some checks failed
Check / eval-and-lint (push) Failing after 6m15s
These existed only to ease muscle-memory from Arch-based distros and add
no behavior on a Nix-native system:

- nomarchy-pkg-install — alias for nomarchy-pkg-add
- nomarchy-pkg-drop    — alias for nomarchy-pkg-remove
- nomarchy-pkg-aur-add — stub that only printed "AUR doesn't exist on NixOS"

The single code caller (nomarchy-voxtype-remove) was removed in the prior
commit, so the shims have no remaining references. SKILL.md updated to use
nomarchy-pkg-add and to point at `nix search nixpkgs` instead of the AUR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:52:19 +01:00
Bernardo Magri
698d8593cd feat: remove unused voxtype dictation feature
Voxtype voice-typing was a full feature (5 scripts, a waybar widget, a
keybind, and a NixOS option) that this install does not use. The
nomarchy.system.voxtype option had no consumer — nothing read it — so the
widget and SUPER+CTRL+X bind were never actually gated by it.

Removes the scripts, default config, the SUPER+CTRL+X bind, the waybar
module (main + summer-night theme + css), the dead option, the installer
comment, and the docs. SCRIPTS.md/KEYBINDINGS.md regenerated; OPTIONS.md
and SKILL.md hand-updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:51:46 +01:00
Bernardo Magri
4b509c271a fix(scripts): remove stale duplicate nomarchy-update
Some checks failed
Check / eval-and-lint (push) Failing after 6m14s
Two scripts named nomarchy-update shipped from different derivations
(core/system/scripts + features/scripts/utils), both landing on PATH so
which one ran depended on PATH ordering. The core/system one never stripped
the install-time `?rev=` pin and rebuilt a nonexistent `#default` attr,
silently keeping installs frozen instead of following rolling main. Keep the
features/scripts/utils version (unpins, queries upstream HEAD, calls
nomarchy-sys-update) and drop the stale one. Regenerated docs/SCRIPTS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:55:04 +01:00
Bernardo Magri
2bdcc0aba0 fix(live): enable VM guest agents so Hyprland boots at full resolution
The nomarchy-live ISO imported ./core and hosts/nomarchy-live.nix but not
core/system/vm-guest.nix, which is the only place spice-vdagentd/qemuGuest
were enabled. Without those agents a VM's virtio-gpu only advertises its
1024x768 fallback mode, so Hyprland's `monitor = , highres, auto` rule picks
that as the highest mode and the live session boots tiny.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:55:04 +01:00
Bernardo Magri
27821c270f fix(impermanence): mark /persist and /home neededForBoot
Some checks failed
Check / eval-and-lint (push) Failing after 5m56s
The impermanence module asserts that the persistent-storage volume and
every volume it bind-mounts into are available in early boot. Persisted
user dirs land under /home, so both /persist and the @home subvolume
must have neededForBoot = true. disko and nixos-generate-config leave it
at the default (false), so a fresh install with impermanence enabled
aborted with:

    All filesystems used for persistent storage must have the option
    "neededForBoot" set to true.
    Please fix the following filesystems: /persist /home

Declare neededForBoot in the module that imposes the requirement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 19:34:04 +01:00
Bernardo Magri
a84e903ae1 fix(live): allow empty-password hyprlock unlock on live ISO
pam_unix rejects empty passwords by default, so if hypridle locked the
live session there was nothing the user could type to unlock it. Set
allowNullPassword on the hyprlock PAM service so a bare Enter dismisses
the locker, matching the empty live account passwords. Scoped to the
live host so installed systems still reject empty unlocks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 19:24:42 +01:00
Bernardo Magri
5ea369d73c fix(installer): declare extra LUKS devices for multi-disk boot
All checks were successful
Check / eval-and-lint (push) Successful in 6m51s
A multi-disk install builds one LUKS container per drive joined into a
single multi-device BTRFS (-d single -m raid1). nixos-generate-config
only emits boot.initrd.luks.devices for the main drive — every subvolume
mounts through /dev/mapper/crypted_main, so the other members are
invisible to it. The installed system then unlocked only the main drive,
the array never assembled, and boot hung. Invisible single-disk, fatal
multi-disk (matches a real-hardware report).

generate_flake_config now writes, into hardware-selection.nix, a
boot.initrd.luks.devices entry for each extra drive (UUID resolved via
disko's partlabel; mapper name mirrors disko-config.nix's sanitize) and
re-asserts the x-systemd.requires= mount options disko sets but which
nixos-generate-config strips.

Verified end-to-end in a 3-disk UEFI QEMU VM: reproduced the bug, applied
the fix, ran a real nixos-install onto the array, and rebooted from the
disks — one passphrase unlocked all three containers, the 3-device BTRFS
assembled, and the system reached login.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:50:10 +01:00
Bernardo Magri
fdd74a0e4f fix(installer): resolve /etc/nomarchy symlink before getFlake
`generate_state` loads `nixpkgs.lib` for the state-schema eval via
`builtins.getFlake "$NOMARCHY_REPO"`. On the live ISO that path is a
symlink chain (/etc/nomarchy -> /etc/static/nomarchy -> /nix/store/…-source),
and Nix 2.31+ rejects getFlake on a symlink ("path '…-source' is a
symlink"), aborting every real-hardware install right after hardware
config generation — single or multi disk.

Resolve the repo to its real store directory with `realpath` at
detection so getFlake receives a plain directory. The dev-checkout
branch already used realpath, so only the live-ISO path was affected.
Reproduced and fixed in a live VM (Nix 2.31.4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:49:43 +01:00
Bernardo Magri
431dd98967 feat(theme): implement declarative Stylix-driven theming for Walker
All checks were successful
Check / eval-and-lint (push) Successful in 6m58s
2026-06-01 21:09:54 +01:00
Bernardo Magri
fa71e03f88 docs: move remaining Ironclad verifications to Shipped
All checks were successful
Check / eval-and-lint (push) Successful in 6m45s
2026-06-01 21:00:09 +01:00
Bernardo Magri
e3002d49ad fix(waybar): ensure update module is visible in summer-night theme
All checks were successful
Check / eval-and-lint (push) Successful in 6m37s
2026-06-01 20:36:04 +01:00
Bernardo Magri
dc7a3e2a29 feat(update): add interactive confirmation prompt to nomarchy-update and expose in all themes
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-06-01 20:35:06 +01:00
Bernardo Magri
dd72110b9a docs: finalize roadmap entries for flake updates and offline resilience
All checks were successful
Check / eval-and-lint (push) Successful in 6m43s
2026-06-01 20:27:25 +01:00
Bernardo Magri
1e6becd42d fix(scripts): improve offline resilience and restore missing update scripts
All checks were successful
Check / eval-and-lint (push) Successful in 7m5s
2026-06-01 20:07:08 +01:00
Bernardo Magri
6ed2ca3d78 fix(scripts): resolve race condition in state-sync and mark stress test as complete
All checks were successful
Check / eval-and-lint (push) Successful in 7m9s
2026-06-01 18:48:17 +01:00
Bernardo Magri
d242d1ea88 fix(waybar): reconcile tooltips with keybindings and fix stale references
Some checks failed
Check / eval-and-lint (push) Failing after 1m52s
2026-05-31 21:59:08 +01:00
Bernardo Magri
3a0164feb7 docs: mark Round 2 Interactive Script Audit as complete
All checks were successful
Check / eval-and-lint (push) Successful in 6m43s
2026-05-31 21:45:41 +01:00
Bernardo Magri
8dc6ba0a21 docs: mark App Integration Polish as complete
All checks were successful
Check / eval-and-lint (push) Successful in 6m38s
2026-05-31 20:59:26 +01:00
Bernardo Magri
5d558a3cc6 fix(system): enable Polkit for Thunar drive management
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:59:04 +01:00
Bernardo Magri
ea34a7ac1f fix(apps): improve Thunar and Mako integration
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:58:10 +01:00
Bernardo Magri
878c11bd41 docs: add RUNTIME_HOOKS.md and link from README
All checks were successful
Check / eval-and-lint (push) Successful in 6m35s
2026-05-31 20:35:58 +01:00
Bernardo Magri
3a42b664a1 feat: wire up post-update and post-install hooks
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:34:01 +01:00
Bernardo Magri
1301873bc0 docs: finalize declarative-state migration in roadmap
Some checks failed
Check / eval-and-lint (push) Failing after 6m45s
2026-05-31 20:09:31 +01:00
Bernardo Magri
dc3346bc55 feat: implement hybrid declarative state with automatic Nix sync
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:09:12 +01:00
Bernardo Magri
624023c1d5 docs: move declarative-state migration batch 1 and monitor scaling to Shipped
Some checks failed
Check / eval-and-lint (push) Failing after 6m42s
2026-05-31 19:33:57 +01:00
Bernardo Magri
130ae80f3e feat: migrate theme and font to declarative Nix options
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 19:33:33 +01:00
Bernardo Magri
a2649728d3 feat(hyprland): implement declarative monitor scaling and add scale picker to welcome wizard
Some checks failed
Check / eval-and-lint (push) Failing after 6m36s
2026-05-31 19:21:52 +01:00
Bernardo Magri
8911ffa4e8 feat(calendar): ship calcurse and wire waybar clock clicks
Some checks failed
Check / eval-and-lint (push) Failing after 6m22s
2026-05-31 16:43:47 +01:00
Bernardo Magri
395c839ed0 fix(waybar): retarget broken clicks in summer-day/night themes to nomarchy scripts
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 16:42:31 +01:00
Bernardo Magri
a9cc39f3fe fix(input-method): update fcitx5-chinese-addons to kdePackages.fcitx5-chinese-addons
All checks were successful
Check / eval-and-lint (push) Successful in 6m51s
2026-05-31 16:12:17 +01:00
Bernardo Magri
e6b7269b3c feat: move core apps to downstream and make app modules opt-in
All checks were successful
Check / eval-and-lint (push) Successful in 6m39s
2026-05-31 15:44:50 +01:00
Bernardo Magri
51a0f920be docs: mark Pillar 9 Component 6 graphical pass as complete 2026-05-31 15:35:30 +01:00
Bernardo Magri
2c86074893 fix(waybar): drop the dead summer-night date on-click (kitty calcurse)
All checks were successful
Check / eval-and-lint (push) Successful in 6m51s
The default theme's clock#date wired on-click to `kitty calcurse`, but
kitty isn't a default-installed app (alacritty is the terminal) and
calcurse isn't installed anywhere — so clicking the date did nothing.
The default config.jsonc uses xdg-terminal-exec/alacritty for its
clicks and summer-day's date has no on-click. Drop the dead action
(tooltip: false), matching summer-day.

Found during the Pillar 9 Component 6 (apps) pass, which confirmed
btop/ghostty/kitty/lazygit/tmux ship config-only by design (the
installer/user picks the binary) — not a bug. Logged two follow-ups:
summer-day's wofi --term=kitty launcher, and shipping a calendar app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 15:16:39 +01:00
Bernardo Magri
43b0cbcd65 fix(waybar): show the battery widget on the default summer-night theme
All checks were successful
Check / eval-and-lint (push) Successful in 6m46s
The summer-night waybar config defined a custom/battery module
(exec nomarchy-battery-status) but never listed it in any modules-*
slot, so it was orphaned and never rendered. summer-night is the
default theme, so a laptop on a stock install had no battery
indicator — the default fallback config and the summer-day theme
both place battery in modules-right; only summer-night omitted it.

Add custom/battery to summer-night's modules-right. The formFactor
filter (laptopOnlyModules) already lists custom/battery, so it shows
on laptop and is dropped on desktop. Found during the Pillar 9
Component 9 (form-factor split) verification pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 15:03:49 +01:00
Bernardo Magri
3d931a6455 fix(menu): repair three dead nomarchy-menu leaf actions (Pillar 9 / Component 3)
All checks were successful
Check / eval-and-lint (push) Successful in 6m41s
Found by driving the menu's observable actions live in a VM with the real
session env imported. All three are also bound to keybindings.

1. nomarchy-hyprland-window-single-square-aspect-toggle (1-Window Ratio,
   SUPER CTRL BACKSPACE): read/set layout:single_window_aspect_ratio, which
   is "no such option" in Hyprland 0.52.1 — the option lives under dwindle:.
   The getoption returned nothing and the keyword set was silently dropped,
   so the toggle did nothing. Point it at dwindle:single_window_aspect_ratio.

2. nomarchy-hyprland-workspace-layout-toggle (Workspace Layout, SUPER L):
   read .tiledLayout off `hyprctl activeworkspace -j` (no such key → always
   null) and switched to "scrolling" (not a built-in layout — hyprctl
   layouts lists only dwindle/master), so it silently no-op'd. Rewrite to
   toggle the real general:layout between dwindle and master.

3. show_setup_menu offered Power Profile unconditionally, but powerprofilesctl
   is absent everywhere (power-profiles-daemon is force-off on laptop in
   favour of TLP and never enabled on desktop), so the entry died with
   "command not found". Gate it behind `command -v powerprofilesctl`.

Verified (1) and (2) against live Hyprland in the VM: aspect flips
[0,0]<->[1,1], layout flips dwindle<->master.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 14:48:37 +01:00
Bernardo Magri
0fbc3d9c82 fix: ground + fix three Pillar-9 candidate bugs from the script sweep
All checks were successful
Check / eval-and-lint (push) Successful in 6m40s
All three confirmed real on inspection.

1. nomarchy-refresh-config was dead on every system: it read
   /etc/nixos/nomarchy/{core/home/config,features} (the source tree never
   lands there — only /etc/nomarchy on VM-guest/live ISO) and fell back to
   ~/.local/share/nomarchy/config, which nothing deployed despite SKILL.md
   documenting it. Deploy the pristine core/home/config tree to
   ~/.local/share/nomarchy/config via xdg.dataFile and read from there, so it
   works without a source checkout; its live caller nomarchy-refresh-fastfetch
   now succeeds. Fix two stale SKILL.md examples (waybar/, hypr/) that pointed
   at non-existent stock paths.

2. nomarchy-docs-scripts shipped a stale divergent copy in
   features/scripts/utils/ (in the user nomarchy-system-scripts package)
   beside the canonical bin/utils dev tool — it errored outside the repo.
   Grounding it surfaced the identical bug in nomarchy-docs-keybindings.
   Delete both duplicates, add them to the generator's self-reference
   denylist so they don't show as false `missing`, regenerate docs/SCRIPTS.md.

3. Home ~/.config/nomarchy/state.json was never seeded on non-installer
   systems, so nomarchy-installed-summary rendered "—" for theme/font/panel.
   Add an idempotent home-manager activation seed in core/home/state.nix that
   backfills the resolved values (defaults * existing — user choices and
   runtime-only keys like welcome_done always win).

Verified: flake check + full eval matrix clean; refresh-config happy/error
paths and the seed deep-merge proven by hand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 14:25:02 +01:00
Bernardo Magri
00b8214cb1 docs(agent): guardrail — never mkDefault a list/attrset option that merges
Some checks failed
Check / eval-and-lint (push) Failing after 7m7s
The "mkDefault everywhere user might override" rule is what caused the
home.packages bug. Clarify: mkDefault is for scalars; on list/attrset
options that other modules contribute to, a mkDefault def is dropped
entirely (filterOverrides) rather than merged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 14:00:29 +01:00
Bernardo Magri
f34f59c2aa fix: drop mkDefault on three more list options it was silently dropping
Some checks failed
Check / eval-and-lint (push) Has been cancelled
Same footgun as the home.packages fix, found by sweeping the tree for
`mkDefault` on list options that other modules contribute to at normal
priority (so filterOverrides discards the mkDefault def entirely):

  * themes/engine/sddm.nix — environment.systemPackages = mkDefault
    [ nomarchy-sddm-theme ] was dropped, so the SDDM greeter theme package
    was never installed (unthemed login).
  * themes/engine/plymouth.nix — boot.kernelParams = mkDefault [ quiet
    splash loglevel=3 … ] was dropped, so boots weren't quiet/clean.
  * features/desktop/waybar/default.nix — home.packages = mkDefault
    [ font-awesome ] was dropped, so waybar's icon font was missing.

Verified via eval: nomarchy-sddm-theme now in systemPackages, "quiet" in
kernelParams, font-awesome in home.packages. Left the genuinely-safe
single-definition mkDefaults alone (plymouth.themePackages,
resolved.fallbackDns, hyprsunset.extraArgs) and the hybridGPU videoDrivers
mkDefault (it outranks nixpkgs' mkOptionDefault on real hardware).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 13:59:46 +01:00
Bernardo Magri
b029edb5b4 docs(roadmap): log the mkDefault-on-home.packages bug found chasing the D-Bus error
Some checks failed
Check / eval-and-lint (push) Failing after 6m51s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:43:41 +01:00
Bernardo Magri
1117dcfc37 fix(home): drop mkDefault on home.packages — it discarded the whole app list
Some checks failed
Check / eval-and-lint (push) Has been cancelled
Chasing a "DBus.Error … name is not activatable" from notify-send (mako),
the root cause turned out to be far bigger: home.packages in
features/default.nix was wrapped in lib.mkDefault. home.packages is a
list, and the module system's filterOverrides keeps only the
highest-priority definitions — so the moment any other module sets
home.packages at normal priority (features/scripts/default.nix's
`[ nomarchy-scripts ]`, the installer's profile packages), this entire
mkDefault list was DROPPED. Every install was silently missing firefox,
thunar, imv, mpv, swww, mako, hyprlock, rofi, etc. — which broke
notifications (mako never installed → exec-once fails → notify-send dead)
and the lock screen (hyprlock missing), and hid a stale `rofi-wayland`
reference (merged into `rofi` upstream) that only errored once the list
was live again. Remove the mkDefault so the list merges (home.packages =
52 pkgs, up from 31; mako/hyprlock/firefox/… now present) and fix
rofi-wayland -> rofi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:40:44 +01:00
Bernardo Magri
6e391b1843 docs(roadmap): log Pillar 9 walker menu verification + two fixes
Some checks failed
Check / eval-and-lint (push) Failing after 6m40s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:05:35 +01:00
Bernardo Magri
c5fe0e09fa fix(walker): background selector must follow symlinks (find -L)
Some checks failed
Check / eval-and-lint (push) Has been cancelled
Pillar 9 VM test: after the menu-registration fix, the walker background
selector menu opened but still showed "No Results". Its GetEntries ran
`find <dir> -type f`, but home-manager deploys the per-theme background
images as symlinks into the nix store, and `-type f` doesn't follow
symlinks. Add -L (the sibling nomarchy_themes.lua already uses `find -L`).
Verified in the VM: `find -type f` -> 0, `find -L` -> the backgrounds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:05:05 +01:00
Bernardo Magri
c831b01b0a fix(walker): deploy elephant theme/background menus where elephant loads them
Some checks failed
Check / eval-and-lint (push) Failing after 6m40s
Pillar 9 VM test: the walker theme picker (and background selector)
returned "No Results". Root cause: nomarchy_themes.lua and
nomarchy_background_selector.lua (elephant lua menu providers named
"nomarchythemes" / "nomarchyBackgroundSelector") were deployed via the
bulk nomarchy config to ~/.config/nomarchy/default/elephant/ — outside
elephant's provider search path, so elephant never registered them and
`nomarchy-launch-walker -m menus:nomarchythemes` (used by nomarchy-theme,
nomarchy-wallpaper, and nomarchy-menu Style submenu) had no backing menu.

Move them into the elephant config (features/apps/elephant/config/menus/)
so they deploy to ~/.config/elephant/menus/. Verified on a fresh VM boot:
`elephant listproviders` now lists menus:nomarchythemes +
menus:nomarchyBackgroundSelector, and the walker theme picker renders all
21 palettes with per-theme preview images.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 09:54:06 +01:00
Bernardo Magri
704cc70c84 docs(roadmap): log full installer end-to-end VM install + boot verification
Some checks failed
Check / eval-and-lint (push) Failing after 6m34s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 09:33:26 +01:00
Bernardo Magri
aaae29fb4d docs(roadmap): log Pillar 9 script sweep + theme visual pass progress
Some checks failed
Check / eval-and-lint (push) Failing after 6m51s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 08:36:00 +01:00
Bernardo Magri
23d432f279 fix(scripts): add python3 so the XPS haptic-touchpad service runs
Some checks failed
Check / eval-and-lint (push) Failing after 6m43s
Pillar 9 VM script sweep: nomarchy-haptic-touchpad failed with
"env: 'python3': No such file or directory". It's a python3 script
(#!/usr/bin/env python3) wired as systemd.services.nomarchy-haptic-touchpad
in core/system/hardware.nix (Dell XPS haptic feedback), run from the
nomarchy-system-scripts wrapper — but python3 wasn't in systemScriptDeps,
so the wrapper's PATH lacked it and the service died on every XPS. Add
python3 (stdlib-only script, plain pkgs.python3 suffices). Verified the
rebuilt wrapper resolves python3 and the script runs to its clean
"no device found" exit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:21:04 +01:00
Bernardo Magri
8e2c27087f docs(roadmap): add Pillar 9 — live VM runtime QA
All checks were successful
Check / eval-and-lint (push) Successful in 7m3s
New pillar to drive the distro running in a VM and verify what actually
works vs. what only looks right in source: run every nomarchy-* command,
walk every menu entry, switch through all 22 palettes and eyeball
rendering, fire every keybinding, drive the installer. Documents the
method (build -> boot headless -> screendump/sendkey) proven in the first
VM-boot pass, the per-component checklist, and the deliverable format. The
old "Pillar 8 runtime verification" Now-board punch-list folds into it.
AGENT.md's verify step now points at the headless VM screenshot flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 21:58:27 +01:00
Bernardo Magri
70d0b56259 docs(roadmap): log Pillar 8 first VM-boot verification pass
Some checks failed
Check / eval-and-lint (push) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 21:53:47 +01:00
92 changed files with 1407 additions and 1167 deletions

View File

@@ -84,7 +84,7 @@ Add user-level packages, aliases, and dotfiles here.
nomarchy.home.terminal = "kitty";
```
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). Hit a rebuild error? Check [Troubleshooting](docs/TROUBLESHOOTING.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). For custom automation, see [Runtime Hooks](docs/HOOKS.md). Hit a rebuild error? Check [Troubleshooting](docs/TROUBLESHOOTING.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
### Applying Changes
After editing your files, apply them instantly. **IMPORTANT:** Nomarchy requires the `--impure` flag for evaluation. You **MUST** use the following aliases rather than standard NixOS commands:

View File

@@ -109,7 +109,7 @@ all_refs=$(grep -rhE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
| grep -oE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
| grep -vE '^(nomarchy-launch|nomarchy-brightness|nomarchy-cmd|nomarchy-pkg|nomarchy-restart|nomarchy-toggle|nomarchy-theme|nomarchy-webapp-handler|nomarchy-font-selector|nomarchy-theme-selector|nomarchy-wallpaper-selector|nomarchy-setup|nomarchy-refresh|nomarchy-scripts|nomarchy-system-scripts|nomarchy-theme-engine-scripts)$' \
| grep -vE '^(nomarchy-plymouth|nomarchy-sddm-theme|nomarchy-live|nomarchy-rev|nomarchy-windows)$' \
| grep -vE '^(nomarchy-eval-matrix)$' \
| grep -vE '^(nomarchy-eval-matrix|nomarchy-docs-scripts|nomarchy-docs-keybindings)$' \
| sort -u)
# The second denylist covers identifiers whose ambiguity survives the line
# filter: `nomarchy-plymouth` / `nomarchy-sddm-theme` are Nix derivation
@@ -117,9 +117,10 @@ all_refs=$(grep -rhE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
# ISO label that shows up in comments, `nomarchy-rev` is `/etc/nomarchy-rev`
# (written by the ISO), and `nomarchy-windows` is a docker container name
# in compose heredocs.
# The third denylist: `nomarchy-eval-matrix` is a bin/utils repo tool (like
# this generator) that names itself in its own header comment — not a
# user-facing script that ships in nomarchy-system-scripts.
# The third denylist: `nomarchy-eval-matrix`, `nomarchy-docs-scripts` (this
# generator), and `nomarchy-docs-keybindings` are bin/utils repo tools that
# name themselves in their own headers / AGENT.md — not user-facing scripts
# that ship in nomarchy-system-scripts.
# --- Render: header --------------------------------------------------------

View File

@@ -57,7 +57,7 @@ This directory contains Nomarchy's source files managed by git. Any changes will
**Reading `~/.local/share/nomarchy/` is SAFE and useful** - do it freely to:
- Understand how nomarchy commands work: `cat $(which nomarchy-theme-set)`
- See default configs before customizing: `cat ~/.local/share/nomarchy/config/waybar/config.jsonc`
- See default configs before customizing: `cat ~/.local/share/nomarchy/config/fastfetch/config.jsonc`
- Check stock theme files to copy for customization
- Reference default hyprland settings: `cat ~/.config/nomarchy/default/hypr/*`
@@ -109,7 +109,7 @@ cat $(which nomarchy-theme-set)
| `nomarchy-install-*` | Install optional software | `nomarchy-install-docker-dbs` |
| `nomarchy-launch-*` | Launch apps | `nomarchy-launch-browser` |
| `nomarchy-cmd-*` | System commands | `nomarchy-cmd-screenshot` |
| `nomarchy-pkg-*` | Package management | `nomarchy-pkg-install <pkg>` |
| `nomarchy-pkg-*` | Package management | `nomarchy-pkg-add <pkg>` |
| `nomarchy-setup-*` | Initial setup tasks | `nomarchy-setup-fingerprint` |
| `nomarchy-update-*` | System updates | `nomarchy-update` |
@@ -334,16 +334,6 @@ nomarchy-tui-remove-all # Bulk-remove every TUI launcher
Both families write `.desktop` files into `~/.local/share/applications/` so they appear in the app launcher (walker / rofi).
### Voice dictation (Voxtype)
```bash
nomarchy-voxtype-install # Install Voxtype + AI model (~150MB)
nomarchy-voxtype-remove # Uninstall Voxtype
nomarchy-voxtype-status # Running state (also shown in waybar)
```
Toggle dictation with `SUPER+CTRL+X` after install.
### Virtualization
```bash
@@ -370,8 +360,9 @@ nomarchy-upload-log
nomarchy-refresh-<app>
# Refresh specific config file
# config-file path is relative to ~/.config/
# eg. nomarchy-refresh-config hypr/hyprlock.conf will refresh ~/.config/hypr/hyprlock.conf
# config-file path is relative to ~/.config/; the stock copy must exist under
# ~/.local/share/nomarchy/config/ (i.e. a core/home/config item).
# eg. nomarchy-refresh-config fastfetch/config.jsonc refreshes ~/.config/fastfetch/config.jsonc
nomarchy-refresh-config <config-file>
# Full reinstall of configs (nuclear option)
@@ -386,7 +377,7 @@ When user requests system changes:
2. **Is it a config edit?** Edit in `~/.config/`, never `~/.local/share/nomarchy/`
3. **Is it a theme customization?** Create a NEW custom theme directory
4. **Is it automation?** Use hooks in `~/.config/nomarchy/hooks/`
5. **Is it a package install?** Use `nomarchy-pkg-add` (or `nomarchy-pkg-aur-add` for AUR-only packages)
5. **Is it a package install?** Use `nomarchy-pkg-add` (adds to `user-packages.json`; rebuild applies it). The AUR does not exist on NixOS — search nixpkgs with `nix search nixpkgs <name>`.
6. **Unsure if command exists?** Search with `compgen -c | grep nomarchy`
## Out of Scope

View File

@@ -51,9 +51,6 @@ bindd = SUPER CTRL, B, Bluetooth controls, exec, nomarchy-launch-bluetooth
bindd = SUPER CTRL, W, Wifi controls, exec, nomarchy-launch-wifi
bindd = SUPER CTRL, T, Activity, exec, nomarchy-launch-tui btop
# Dictation
bindd = SUPER CTRL, X, Toggle dictation, exec, voxtype record toggle
# Zoom
bindd = SUPER CTRL, Z, Zoom in, exec, hyprctl keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor -j | jq '.float + 1')
bindd = SUPER CTRL ALT, Z, Reset zoom, exec, hyprctl keyword cursor:zoom_factor 1

View File

@@ -22,12 +22,15 @@ layer=overlay
[summary~="Setup Wi-Fi"]
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Setup Wi-Fi"; nomarchy-launch-wifi'
on-click=exec sh -c 'nomarchy-notification-dismiss "Setup Wi-Fi"; nomarchy-launch-wifi'
[summary~="Update System"]
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Update System"; nomarchy-launch-floating-terminal-with-presentation nomarchy-update'
on-click=exec sh -c 'nomarchy-notification-dismiss "Update System"; nomarchy-launch-floating-terminal-with-presentation nomarchy-update'
[summary~="Learn Keybindings"]
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Learn Keybindings"; nomarchy-menu-keybindings'
on-click=exec sh -c 'nomarchy-notification-dismiss "Learn Keybindings"; nomarchy-menu-keybindings'
[summary~="Screenshot copied & saved"]
max-icon-size=80

View File

@@ -1,97 +0,0 @@
# Voxtype Configuration
#
# Location: ~/.config/voxtype/config.toml
# All settings can be overridden via CLI flags
#
# State file for external integrations (Waybar, polybar, etc.)
# Use "auto" for default location ($XDG_RUNTIME_DIR/voxtype/state),
# a custom path, or "disabled" to turn off. The daemon writes state
# ("idle", "recording", "transcribing") to this file whenever it changes.
# Required for `voxtype record toggle` and `voxtype status` commands.
state_file = "auto"
[hotkey]
# Hotkey is configured in Hyprland. Default is Super + Ctrl + X
enabled = false
[audio]
# Audio input device ("default" uses system default)
# List devices with: pactl list sources short
device = "default"
# Sample rate in Hz (whisper expects 16000)
sample_rate = 16000
# Maximum recording duration in seconds (safety limit)
max_duration_secs = 60
# [audio.feedback]
# Enable audio feedback sounds (beeps when recording starts/stops)
# enabled = true
#
# Sound theme: "default", "subtle", "mechanical", or path to custom theme directory
# theme = "default"
#
# Volume level (0.0 to 1.0)
# volume = 0.7
[whisper]
# Model to use for transcription
# Options: tiny, tiny.en, base, base.en, small, small.en, medium, medium.en, large-v3, large-v3-turbo
# .en models are English-only but faster and more accurate for English
# large-v3-turbo is faster than large-v3 with minimal accuracy loss (recommended for GPU)
# Or provide absolute path to a custom .bin model file
model = "base.en"
# Language for transcription
# Use "en" for English, "auto" for auto-detection
# See: https://github.com/openai/whisper#available-models-and-languages
language = "en"
# Translate non-English speech to English
translate = false
# Number of CPU threads for inference (omit for auto-detection)
# threads = 4
[output]
# Primary output mode: "type" or "clipboard"
# - type: Simulates keyboard input at cursor position (requires ydotool)
# - clipboard: Copies text to clipboard (requires wl-copy)
mode = "type"
# Fall back to clipboard if typing fails
fallback_to_clipboard = true
# Delay between typed characters in milliseconds
# 0 = fastest possible, increase if characters are dropped
type_delay_ms = 1
# Post-processing command (optional)
# Pipe transcribed text through an external command for cleanup before output.
# The command receives text on stdin and outputs processed text on stdout.
# Useful for LLM-based text cleanup, grammar correction, filler word removal.
# On any failure (timeout, error), falls back to original transcription.
#
# [output.post_process]
# command = "ollama run llama3.2:1b 'Clean up this dictation. Fix grammar, remove filler words. Output only the cleaned text:'"
# timeout_ms = 30000 # 30 second timeout (generous for LLM)
[output.notification]
# Show notification when recording starts (hotkey pressed)
on_recording_start = false
# Show notification when recording stops (transcription beginning)
on_recording_stop = false
# Show notification with transcribed text after transcription completes
on_transcription = false
# [text]
# Text processing options (word replacements, spoken punctuation)
#
# Enable spoken punctuation conversion (e.g., say "period" to get ".")
# spoken_punctuation = false
#
# Custom word replacements (case-insensitive)
# replacements = { "hyperwhisper" = "hyprwhspr" }

View File

@@ -0,0 +1,9 @@
#!/bin/bash
# This hook is called after the nomarchy-welcome wizard finishes successfully.
# Use it to run any custom first-boot logic (e.g. cloning your dotfiles repo,
# setting up SSH keys, or launching specific initial apps).
# Example: Open the manual and show a notification
# nomarchy-manual
# notify-send "Welcome home" "Your Nomarchy system is ready to use."

View File

@@ -95,4 +95,12 @@ in
home.file.".XCompose" = lib.mkDefault {
source = ./config/nomarchy/default/xcompose;
};
# Pristine copy of the stock configs, kept out of ~/.config so a user edit
# never touches it. `nomarchy-refresh-config` restores a file in ~/.config
# from here, and SKILL.md points users at it to diff against defaults.
xdg.dataFile."nomarchy/config".source = builtins.path {
name = "nomarchy-config";
path = ./config;
};
}

View File

@@ -42,7 +42,11 @@ in
theme = lib.mkOption {
type = lib.types.str;
default = schema.home.theme;
description = "System theme name.";
description = ''
System theme name. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the entire desktop.
'';
};
formFactor = lib.mkOption {
type = lib.types.enum [ "laptop" "desktop" ];
@@ -84,35 +88,56 @@ in
wallpaper = lib.mkOption {
type = lib.types.str;
default = schema.home.wallpaper;
description = "System wallpaper path.";
description = ''
System wallpaper path. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the background.
'';
};
panelPosition = lib.mkOption {
type = lib.types.enum [ "top" "bottom" ];
default = schema.home.panelPosition;
description = "Waybar panel position.";
description = ''
Waybar panel position. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the UI.
'';
};
hyprland = {
gaps_in = lib.mkOption {
type = lib.types.int;
default = schema.home.hyprland.gaps_in;
description = "Inner gaps for Hyprland.";
description = "Inner gaps for Hyprland. Driven by the declarative state.";
};
gaps_out = lib.mkOption {
type = lib.types.int;
default = schema.home.hyprland.gaps_out;
description = "Outer gaps for Hyprland.";
description = "Outer gaps for Hyprland. Driven by the declarative state.";
};
border_size = lib.mkOption {
type = lib.types.int;
default = schema.home.hyprland.border_size;
description = "Border size for Hyprland.";
description = "Border size for Hyprland. Driven by the declarative state.";
};
scale = lib.mkOption {
type = lib.types.str;
default = schema.home.hyprland.scale;
description = ''
Default monitor scale. Use "auto" to let Hyprland decide,
or a numeric string like "1", "1.25", "1.5", "2". Driven
by the declarative state.
'';
};
};
fonts = {
monospace = lib.mkOption {
type = lib.types.str;
default = schema.home.font;
description = "System monospace font.";
description = ''
System monospace font. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the entire desktop.
'';
};
};
iconsTheme = lib.mkOption {
@@ -144,14 +169,25 @@ in
};
apps = {
opencode = {
enable = lib.mkEnableOption ''
opencode AI coding CLI integration. When on, deploys
~/.config/opencode/opencode.json. The `opencode` package itself
is not installed by Nomarchy add it to your home.nix if you
want it on PATH.
'';
alacritty.enable = lib.mkEnableOption "Alacritty terminal integration";
btop.enable = lib.mkEnableOption "btop resource monitor integration";
ghostty.enable = lib.mkEnableOption "Ghostty terminal integration";
kitty.enable = lib.mkEnableOption "Kitty terminal integration";
lazygit.enable = lib.mkEnableOption "lazygit integration";
tmux.enable = lib.mkEnableOption "tmux integration";
elephant.enable = lib.mkEnableOption "Elephant menu provider integration";
walker.enable = lib.mkEnableOption "Walker launcher integration";
swayosd.enable = lib.mkEnableOption "SwayOSD integration";
vscode = {
enable = lib.mkEnableOption "VSCode integration";
devExtensions = lib.mkEnableOption "Nomarchy's curated VSCode extension pack (language servers + git + editor enhancements)";
};
opencode.enable = lib.mkEnableOption ''
opencode AI coding CLI integration. When on, deploys
~/.config/opencode/opencode.json. The `opencode` package itself
is not installed by Nomarchy add it to your home.nix if you
want it on PATH.
'';
};
gaming = {

View File

@@ -1,4 +1,4 @@
{ config, lib, ... }:
{ config, lib, pkgs, ... }:
let
nomarchyLib = import ../../lib { inherit lib; };
@@ -12,41 +12,54 @@ let
# Read unified state from ~/.config/nomarchy/state.json
togglesState = nomarchyLib.readHomeState config.home.homeDirectory;
cfg = config.nomarchy;
# The resolved values, in state.json's on-disk shape. Seeded into the file
# so runtime consumers (nomarchy-installed-summary, the setters) see the
# real defaults instead of a missing key — without this the summary shows
# "—" for theme/font/panel on any system the installer didn't write to.
seedJSON = builtins.toJSON {
inherit (cfg) theme wallpaper panelPosition nightlightTemperature;
font = cfg.fonts.monospace;
inherit (cfg.toggles) suspend screensaver idle nightlight waybar;
inherit (cfg.hyprland) gaps_in gaps_out border_size;
};
stateFile = "${config.home.homeDirectory}/.config/nomarchy/state.json";
in
{
# Every assignment uses lib.mkDefault so a downstream /etc/nixos/home.nix
# can override the state.json-derived value. Without mkDefault, every
# option here would resolve at default priority and conflict on
# assignment from the user's config.
# can override the value. The Nix options are now the declarative source
# of truth. state.json is purely seeded to keep runtime scripts (menu,
# summary) in sync with the Nix-level state.
config = {
nomarchy = {
toggles = {
suspend = lib.mkDefault (togglesState.suspend or schema.home.suspend);
screensaver = lib.mkDefault (togglesState.screensaver or schema.home.screensaver);
idle = lib.mkDefault (togglesState.idle or schema.home.idle);
nightlight = lib.mkDefault (togglesState.nightlight or schema.home.nightlight);
waybar = lib.mkDefault (togglesState.waybar or schema.home.waybar);
};
nightlightTemperature = lib.mkDefault (togglesState.nightlightTemperature or schema.home.nightlightTemperature);
theme = lib.mkDefault (togglesState.theme or schema.home.theme);
wallpaper = lib.mkDefault (togglesState.wallpaper or schema.home.wallpaper);
panelPosition = lib.mkDefault (togglesState.panelPosition or schema.home.panelPosition);
hyprland = {
gaps_in = lib.mkDefault (togglesState.hyprland.gaps_in or schema.home.hyprland.gaps_in);
gaps_out = lib.mkDefault (togglesState.hyprland.gaps_out or schema.home.hyprland.gaps_out);
border_size = lib.mkDefault (togglesState.hyprland.border_size or schema.home.hyprland.border_size);
};
fonts.monospace = lib.mkDefault (togglesState.font or schema.home.font);
# Derived properties from the theme directory
isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or schema.home.theme;
themeName = cfg.theme;
inherit assetsPath;
});
iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme {
themeName = togglesState.theme or schema.home.theme;
themeName = cfg.theme;
inherit assetsPath;
});
};
# Backfill any state.json key the user hasn't set. Existing values always
# win (`defaults * existing`), so a user's theme/font/etc. and any
# runtime-only keys (welcome_done, …) are never clobbered — this only
# fills the gaps so the file reflects the system's actual defaults.
home.activation.seedNomarchyState = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
run mkdir -p "$(dirname ${lib.escapeShellArg stateFile})"
tmp="$(${pkgs.coreutils}/bin/mktemp)"
# Build the next file contents in $tmp first; only the final move is
# guarded by `run` so a dry-run never mutates the real state file.
if [ -e ${lib.escapeShellArg stateFile} ]; then
${pkgs.jq}/bin/jq -n --argjson d ${lib.escapeShellArg seedJSON} \
--slurpfile s ${lib.escapeShellArg stateFile} '$d * $s[0]' > "$tmp"
else
printf '%s\n' ${lib.escapeShellArg seedJSON} > "$tmp"
fi
run mv "$tmp" ${lib.escapeShellArg stateFile}
'';
};
}

View File

@@ -18,6 +18,7 @@
./network.nix
./impermanence.nix
./browser.nix
./file-manager.nix
# Tier 1 system features (all opt-in via nomarchy.system.*).
./snapper.nix
./laptop.nix

View File

@@ -0,0 +1,32 @@
{ pkgs, lib, ... }:
{
# Core system-side support for file management.
# This provides the backend services required by Thunar (and other file
# managers) to function correctly on a standalone compositor.
services.gvfs.enable = lib.mkDefault true; # Mount, trash, and other file system operations
services.tumbler.enable = lib.mkDefault true; # Thumbnail support for images/videos/etc.
# Explicitly enable Thunar for D-Bus integration
programs.thunar.enable = lib.mkDefault true;
# Required for drive management (mount/unmount) and other privileged actions
security.polkit.enable = true;
# Allow Thunar to use gvfs (trash, network mounts, etc.)
programs.thunar.plugins = with pkgs.xfce; [
thunar-archive-plugin
thunar-volman
thunar-media-tags-plugin
];
# Supporting utilities for Thunar and general file management
environment.systemPackages = with pkgs; [
ffmpegthumbnailer # Video thumbnails
libgsf # ODF thumbnails
poppler-utils # PDF thumbnails
xfce.exo # Required for "Open Terminal Here" and other associations
shared-mime-info # Standard MIME database
];
}

View File

@@ -87,6 +87,17 @@ in
'';
};
# The impermanence module asserts that every filesystem it touches is
# available in early boot: the persistent-storage volume (/persist) and
# any volume it bind-mounts *into* — the user-persistence dirs land under
# /home, so the @home subvolume counts too. disko and
# nixos-generate-config leave neededForBoot at its default (false) on
# these subvolumes, which trips the assertion ("Please fix the following
# filesystems: /persist /home"). Declare it here, where the requirement
# originates.
fileSystems."/persist".neededForBoot = true;
fileSystems."/home".neededForBoot = true;
# 2. Persistence Configuration: What survives the wipe
environment.persistence."/persist" = {
hideMounts = true;

View File

@@ -11,12 +11,15 @@ in
i18n.inputMethod = {
enable = true;
type = "fcitx5";
fcitx5.addons = with pkgs; [
fcitx5-mozc # Japanese
fcitx5-chinese-addons # Chinese (pinyin/shuangpin)
fcitx5-table-extra # Hanyu/Cangjie/etc.
fcitx5-gtk # GTK4/3 client
];
fcitx5 = {
waylandFrontend = true;
addons = with pkgs; [
fcitx5-mozc # Japanese
kdePackages.fcitx5-chinese-addons
fcitx5-table-extra # Hanyu/Cangjie/etc.
fcitx5-gtk # GTK4/3 client
];
};
};
};
}

View File

@@ -61,7 +61,10 @@ in
theme = lib.mkOption {
type = lib.types.str;
default = schema.system.theme;
description = "Selected system theme.";
description = ''
Selected system theme. This is the declarative source of truth for
system-level components (SDDM, Plymouth, browser policies).
'';
};
# ----- Tier 1 system features (all opt-in, no behavioural change off) ---
@@ -203,14 +206,5 @@ in
most users want this off.
'';
};
voxtype = {
enable = lib.mkEnableOption ''
voxtype voice-typing integration. NOTE: voxtype is not packaged in
nixpkgs when enabled, install voxtype yourself (e.g. via
`home.packages = [ (pkgs.callPackage {}) ]`). With this off the
SUPER+CTRL+X keybinding and waybar widget are no-ops.
'';
};
};
}

View File

@@ -33,6 +33,7 @@ let
fwupd
hyprland
swayosd
python3 # nomarchy-haptic-touchpad is a python3 script (Dell XPS service)
];
in
pkgs.stdenv.mkDerivation {

View File

@@ -21,8 +21,7 @@ else
notify-send -u low " Stop locking computer when idle"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.idle = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write idle "$NEW_VALUE" --type bool
echo "Idle state set to $NEW_VALUE. Environment will be fully updated on next rebuild."

View File

@@ -1,41 +0,0 @@
#!/usr/bin/env bash
# Nomarchy Update Script
# 1. Updates the flake inputs in the repository
# 2. Applies system-wide NixOS changes
# 3. Applies user-level Home Manager changes (Standalone)
set -e
# Detect the repository location
if [ -f "/etc/nixos/flake.nix" ]; then
REPO_DIR="/etc/nixos"
elif [ -f "/etc/nomarchy/flake.nix" ]; then
REPO_DIR="/etc/nomarchy"
else
echo "Error: Nomarchy flake repository not found in /etc/nixos or /etc/nomarchy."
exit 1
fi
echo "--- Starting Nomarchy Update from $REPO_DIR ---"
# 1. Update Flake Lock
echo "Updating flake inputs..."
sudo nix flake update --flake "$REPO_DIR"
# 2. Rebuild System
echo "Applying system-level updates..."
sudo nixos-rebuild switch --flake "$REPO_DIR#default" --impure
# 3. Rebuild Home Environment (Standalone)
echo "Applying user-level updates..."
home-manager switch --flake "$REPO_DIR#$USER" --impure
# 4. Commit changes if it's a git repo
if [ -d "$REPO_DIR/.git" ]; then
echo "Committing update to local history..."
sudo git -C "$REPO_DIR" add flake.lock
sudo git -C "$REPO_DIR" commit -m "chore: update system (flake.lock)" || echo "No lockfile changes to commit."
fi
echo "--- Nomarchy Update Complete ---"

View File

@@ -10,20 +10,8 @@ in
{
# Every assignment is lib.mkDefault so a downstream /etc/nixos/system.nix
# can still set e.g. `nomarchy.system.features.hybridGPU = true;`
# without colliding with the state.json-derived value. Without
# mkDefault, every state.json-driven option was unoverridable from
# Nix — flipping hybridGPU required jq'ing the state file rather
# than declaring it in your config.
# without colliding with the default values. The Nix options are now the
# declarative source of truth.
config.nomarchy.system = {
dns = lib.mkDefault (systemState.dns or schema.system.dns);
customDns = lib.mkDefault (systemState.customDns or schema.system.customDns);
wifi.powersave = lib.mkDefault (systemState.wifi.powersave or schema.system.wifi.powersave);
timezone = lib.mkDefault (systemState.timezone or schema.system.timezone);
features = {
fingerprint = lib.mkDefault (systemState.features.fingerprint or schema.system.features.fingerprint);
fido2 = lib.mkDefault (systemState.features.fido2 or schema.system.features.fido2);
hybridGPU = lib.mkDefault (systemState.features.hybridGPU or schema.system.features.hybridGPU);
};
theme = lib.mkDefault (systemState.theme or schema.system.theme);
};
}

View File

@@ -64,10 +64,12 @@ bin/ Convenience wrappers for testing (nomarchy-test-installer, …).
When you add a new feature:
- A new app → `features/apps/<name>/default.nix` (+ optional `config/`), import it from `features/default.nix`.
- A new app → `features/apps/<name>/default.nix` (+ optional `config/`), import it from `features/default.nix`. **All app modules must be opt-in** via a `nomarchy.apps.<name>.enable` option defined in `core/home/options.nix`. Enabling the module should also handle the `programs.<name>.enable` or `home.packages` installation where applicable.
- A new system service → `core/system/<name>.nix`, import from `core/default.nix`.
- A new toggle → add to `core/system/options.nix` or `core/home/options.nix`, wire it into the relevant module, document it in `docs/OPTIONS.md`.
Core user-facing apps (browsers, file managers, media players) are **not** installed by the `features/default.nix` module list. Instead, they are managed in the downstream `home.nix` (which the installer pre-fills with defaults like `firefox` and `thunar`). This keeps the Nomarchy core modules focused on the desktop environment while giving the user explicit control over their app set.
---
## 3. Guardrails (non-negotiable unless the user overrides)
@@ -77,7 +79,7 @@ These are inherited from the established Nomarchy conventions. Violating them is
1. **Declarative-first.** No imperative state in `core/`. Mutable state goes in `~/.config/nomarchy/state.json` or in NixOS / home-manager options.
2. **Downstream-flake friendly.** Every behavior toggle is a `nomarchy.*` option documented in `docs/OPTIONS.md`. Adding a feature without a corresponding option is a bug.
3. **Opt-in by default.** New features default off (or default to existing behavior). The installer can flip defaults *for the user being installed*, but the option must read sensibly when set by hand.
4. **`lib.mkDefault` everywhere user might override.** If a downstream `system.nix` would reasonably want to change something Nomarchy sets, set it with `lib.mkDefault`. If it must not be overridden, use `lib.mkForce` and explain why in a comment.
4. **`lib.mkDefault` on *scalars* the user might override — never on lists/attrsets that merge.** If a downstream `system.nix` would reasonably want to change a scalar Nomarchy sets (a string, bool, int), set it with `lib.mkDefault`. If it must not be overridden, use `lib.mkForce` and explain why. **But do not wrap a list or attrset option (`home.packages`, `environment.systemPackages`, `boot.kernelParams`, …) in `lib.mkDefault`** when other modules also contribute to it: the module system's `filterOverrides` keeps only the highest-priority definitions, so a `mkDefault` list is *silently discarded the moment any module sets that option at normal priority* — it doesn't merge, it vanishes. This dropped the entire curated `home.packages` (firefox, mako, hyprlock, …) and broke notifications + the lock screen distro-wide (`1117dcf`, `f34f59c`). A plain list/attrset is the correct default — it merges, and downstream can still add to it (to *remove* a default, downstream uses `mkForce`/filtering, which is rare).
5. **Reuse before invent.** ~155 `nomarchy-*` scripts already exist across `core/system/scripts/`, `features/scripts/utils/`, `themes/engine/scripts/`. Grep before writing a new one.
6. **No comments that narrate.** Don't write comments explaining *what* the code does. Only write a comment when the *why* is non-obvious — a hidden constraint, a subtle invariant, a workaround.
7. **No backwards-compat shims.** If you remove a thing, remove it everywhere. No re-exports, no `// removed` markers.
@@ -156,7 +158,7 @@ Steps you should follow for any non-trivial change:
```bash
sudo /etc/install.sh --dry-run # in the live ISO or VM
```
For waybar / Hyprland visual changes, the only reliable check is booting the live ISO with `nomarchy-test-live-iso`. If you can't boot it, **say so** rather than claiming success.
For waybar / Hyprland visual changes, boot it and look. On a host with KVM you can do this headless: `nix build .#nixosConfigurations.default.config.system.build.vm`, run its `bin/run-*-vm` with `QEMU_OPTS="-display none -monitor unix:/tmp/mon.sock,server,nowait -vga none"`, then `echo "screendump /tmp/s.ppm" | socat - unix-connect:/tmp/mon.sock`, convert PPM→PNG, and inspect it (this is how the first Pillar 9 pass caught the boot-time Hyprland config-error overlay). The `nomarchy-test-live-iso` / `installerVm` flows are the ISO-side equivalents. If you genuinely can't boot anything, **say so** rather than claiming a visual change works. See ROADMAP §9 (Live VM runtime QA) for the full method.
6. **Commit narrowly.** One concept per commit. The commit subject is `<type>: <imperative summary>` (`feat:`, `fix:`, `docs:`, `chore:`). The body explains the why.
7. **Push only when the user asks.** Local commits are free; pushing publishes.
@@ -199,9 +201,16 @@ in { ... }
`nomarchy.system.formFactor` and `nomarchy.formFactor` are the two halves of the same flag (system + home). Default `"laptop"`. The installer auto-detects via `compgen -G "/sys/class/power_supply/BAT*"` and writes the explicit value into both generated files. Use this option to gate any laptop-only UI / service.
### State (`state.json`)
### Hybrid State (`state.json` + `nomarchy-state.nix`)
Theme, font, wallpaper, and a few feature toggles live in `~/.config/nomarchy/state.json` so they can change without a rebuild. Schema is in `lib/state-schema.nix`. The Home Manager evaluator reads it via `lib/default.nix`. **Don't add new state without justifying it** — most "state" should be a NixOS option instead.
Nomarchy uses a hybrid model to bridge the gap between runtime UI discovery and declarative Nix persistence.
1. **Runtime (`state.json`):** Located at `~/.config/nomarchy/state.json`. Consumed by scripts for instant session reloads (Waybar, Walker, etc.).
2. **Declarative (`nomarchy-state.nix`):** Located at `/etc/nixos/nomarchy-state.nix`. This file is the primary authority for Nix evaluation and is imported by `home.nix`.
3. **The Sync:** Whenever a script calls `nomarchy-state-write`, the `nomarchy-sync-nix-state` helper is triggered. It mirrors the current UI state into the `.nix` file automatically.
4. **Solidification:** To make a UI change permanent, the user (or script) runs `nomarchy-env-update`, which performs a fast `home-manager switch`.
When adding new configuration options that should be script-manageable, ensure they are added to the sync logic in `features/scripts/utils/nomarchy-sync-nix-state`.
### Scripts derivation

57
docs/HOOKS.md Normal file
View File

@@ -0,0 +1,57 @@
# Nomarchy Runtime Hooks
Hooks allow you to run custom bash scripts when specific system events occur. They are the primary way to extend Nomarchy's behavior without modifying the core Nix configuration.
## 1. How it works
Hooks are simple bash scripts located in `~/.config/nomarchy/hooks/`.
When a supported event occurs, Nomarchy checks for a file with the corresponding name in that directory. If the file exists, it is executed.
- **Location:** `~/.config/nomarchy/hooks/<hook-name>`
- **Language:** Bash (or any executable script with a shebang).
- **Execution:** Synchronous. The calling Nomarchy script waits for your hook to finish.
## 2. Available Hooks
| Hook Name | Triggered when... | Arguments |
| --- | --- | --- |
| `post-install` | The `nomarchy-welcome` wizard finishes. | None |
| `post-update` | `nomarchy-env-update` finishes successfully. | None |
| `theme-set` | A new system theme is applied. | `$1`: theme-id (e.g. `nord`) |
| `font-set` | A new system font is applied. | `$1`: font name (e.g. `JetBrainsMono Nerd Font`) |
| `battery-low` | Battery drops below 15% (laptop only). | `$1`: Current percentage |
## 3. Examples
### Auto-sync dotfiles after update
Create `~/.config/nomarchy/hooks/post-update`:
```bash
#!/bin/bash
notify-send "Nomarchy" "Syncing personal dotfiles..."
git -C ~/Projects/dotfiles pull && git -C ~/Projects/dotfiles push
```
### Apply custom color overrides after theme change
Create `~/.config/nomarchy/hooks/theme-set`:
```bash
#!/bin/bash
# Re-apply a specific transparency hack that Stylix might have overwritten
hyprctl keyword decoration:active_opacity 0.95
```
## 4. Samples
Nomarchy ships sample hooks with the `.sample` extension. You can use these as a starting point by copying them:
```bash
cp ~/.config/nomarchy/hooks/post-install.sample ~/.config/nomarchy/hooks/post-install
chmod +x ~/.config/nomarchy/hooks/post-install
```
## 5. Development
If you want to trigger a hook manually for testing:
```bash
nomarchy-hook <name> [args...]
```

View File

@@ -52,7 +52,6 @@ _Source: `core/home/config/nomarchy/default/hypr/bindings/utilities.conf`_
| SUPER CTRL | B | Bluetooth controls |
| SUPER CTRL | W | Wifi controls |
| SUPER CTRL | T | Activity |
| SUPER CTRL | X | Toggle dictation |
| SUPER CTRL | Z | Zoom in |
| SUPER CTRL ALT | Z | Reset zoom |
| SUPER CTRL | L | Lock system |

View File

@@ -122,18 +122,45 @@ create minimal versions to start:
```nix
{ pkgs, ... }:
{
imports = [
# Machine-managed state (theme, font, toggles).
# UI scripts update this file automatically.
./nomarchy-state.nix
];
home.packages = with pkgs; [
btop
firefox
xfce.thunar
imv
mpv
fastfetch
chromium
# …add anything you want; firefox/thunar/mpv/mako/etc. ship with Nomarchy.
# …add anything you want; mako/hyprlock/swww/rofi/etc. ship with Nomarchy.
];
# Enable Nomarchy's curated app configurations
nomarchy.apps = {
alacritty.enable = true;
btop.enable = true;
elephant.enable = true;
swayosd.enable = true;
walker.enable = true;
vscode.enable = true;
};
}
```
Since you are migrating manually, you'll need to generate the initial
`nomarchy-state.nix` so the import doesn't fail. Run this once:
```bash
# Seed the initial Nix state from the defaults
nomarchy-sync-nix-state
```
Move any user/services/packages you had in `configuration.nix` over to
`system.nix`. Do **not** redefine things Nomarchy already provides (display
manager, Hyprland, PipeWire, NetworkManager) unless you want to override
...manager, Hyprland, PipeWire, NetworkManager) unless you want to override
them — see the [conflicts](#conflicts-to-resolve-before-rebuild) section.
## 3. (Optional) Pick up hardware-specific tuning
@@ -242,6 +269,20 @@ The ISO autologins to a Hyprland live session that points you at:
After install, the system at `/etc/nixos/` is the same shape this guide
produces by hand.
**Multi-disk (BTRFS RAID) caveat.** If you select more than one target drive,
disko builds a multi-device BTRFS spanning one LUKS container per disk. The
extra containers are invisible to `nixos-generate-config` (every subvolume
mounts via the *main* `/dev/mapper/crypted_main`, so it only ever emits a
`boot.initrd.luks.devices` entry for the main drive). The installer therefore
writes the missing entries — plus the `x-systemd.requires=` mount options that
make systemd-initrd wait for every member — into `hardware-selection.nix`.
If you hand-write a multi-disk flake instead of using the installer, you must
add, for each extra drive, `boot.initrd.luks.devices."<mapper>".device =
"/dev/disk/by-uuid/<luks-partition-uuid>";` (mapper name = `crypted_` + the
device path with `/` and `-` replaced by `_`, e.g. `/dev/sdb` →
`crypted__dev_sdb`); without it the array can't be assembled and the system
hangs at boot.
---
## Verification (in-place migration)

View File

@@ -129,10 +129,6 @@ Without prime config, supergfxd still switches modes but render-offload via `nvi
`bool`, default `false`. fcitx5 input method for CJK / IME. Wires NixOS's `i18n.inputMethod` and autostarts `fcitx5-daemon`.
### `nomarchy.system.voxtype.enable`
`bool`, default `false`. voxtype voice-typing integration. voxtype isn't packaged in nixpkgs — when you enable this, install voxtype yourself (e.g. `home.packages = [ (pkgs.callPackage … {}) ]`). With this off the `SUPER+CTRL+X` keybinding and waybar widget are no-ops.
### `nomarchy.hardware.isXPS`
`bool`, default `false`. Dell XPS fixes — haptic touchpad service and PCI/I²C power-control udev rules.
@@ -253,6 +249,50 @@ Without prime config, supergfxd still switches modes but render-offload via `nvi
`nullOr path`, default `null`. Path to a replacement config directory. When set, the items listed in `core/home/configs.nix` (`fastfetch`, `fcitx5`, `fontconfig`, `git`, `imv`, `nautilus-python`, `nomarchy`, `nomarchy-skill`, `uwsm`, `wiremix`, plus the loose files) are read from `<this-path>/<name>` instead of the bundled defaults. Distinct from `nomarchy.overrides.*` below — `configOverrides` is a working *bulk* redirect; `overrides.paths` is a *per-file* attrset map.
### `nomarchy.apps.alacritty.enable`
`bool`, default `false`. Enables Nomarchy's curated Alacritty configuration. The installer enables this by default.
### `nomarchy.apps.btop.enable`
`bool`, default `false`. Enables Nomarchy's curated btop configuration (themed color scheme, vim keys). The installer enables this by default.
### `nomarchy.apps.ghostty.enable`
`bool`, default `false`. Deploys Nomarchy's curated Ghostty configuration.
### `nomarchy.apps.kitty.enable`
`bool`, default `false`. Deploys Nomarchy's curated Kitty configuration.
### `nomarchy.apps.lazygit.enable`
`bool`, default `false`. Deploys Nomarchy's curated lazygit configuration.
### `nomarchy.apps.tmux.enable`
`bool`, default `false`. Deploys Nomarchy's curated tmux configuration.
### `nomarchy.apps.elephant.enable`
`bool`, default `false`. Enables Nomarchy's curated Elephant menu provider integration (required for the theme picker and background selector). The installer enables this by default.
### `nomarchy.apps.walker.enable`
`bool`, default `false`. Enables Nomarchy's curated Walker launcher configuration. The installer enables this by default.
### `nomarchy.apps.swayosd.enable`
`bool`, default `false`. Enables Nomarchy's curated SwayOSD configuration. The installer enables this by default.
### `nomarchy.apps.vscode.enable`
`bool`, default `false`. Enables Nomarchy's curated VSCode configuration (theming, fonts). The installer enables this by default.
### `nomarchy.apps.vscode.devExtensions`
`bool`, default `false`. Install Nomarchy's curated VSCode extension pack (language servers + git + editor enhancements). The palette theme extensions are *always* installed when `vscode.enable` is true — every palette except `ethereal`, `hackerman`, and `vantablack` resolves its `workbench.colorTheme` via either `pkgs.vscode-extensions` (catppuccin/nord/tokyo-night/rose-pine/gruvbox) or `pkgs.vscode-utils.extensionFromVscodeMarketplace` with version + sha256 pinned (sainnhe.everforest, qufiwefefwoyn.kanagawa, monokai.theme-monokai-pro-vscode, oldjobobo.{lumon,miasma,retro-82}, shadesOfBuntu.flexoki-light, jovejonovski.ocean-green, TahaYVR.matteblack, Bjarne.white-theme). The three palettes whose theme extension isn't on the marketplace fall back to VSCode's default theme; see the ROADMAP Later row.
### `nomarchy.apps.opencode.enable`
`bool`, default `false`. opencode AI coding CLI integration. Deploys `~/.config/opencode/opencode.json`. The `opencode` package itself is **not** installed by Nomarchy — add it to your `home.packages`.
@@ -263,11 +303,7 @@ Without prime config, supergfxd still switches modes but render-offload via `nvi
### `nomarchy.accessibility.enable`
`bool`, default `false`. Home-side companion to `nomarchy.system.accessibility.enable`. Three Hyprland-side adjustments: slows `input.repeat_rate` to `25` (default `40`) and `input.repeat_delay` to `1000` ms (default `600`) so holding a key isn't a runaway machine-gun for low-mobility users; binds `SUPER+ALT+S` to launch the Orca screen reader (the system preset already puts `orca` on PATH). Set to the same value as the system option. Update the bullet in `docs/ROADMAP.md` if a high-contrast palette ships as a `themes/palettes/` entry — it'll be gated on this option.
### `nomarchy.vscode.devExtensions`
`bool`, default `false`. Install Nomarchy's curated VSCode extension pack (language servers + git + editor enhancements). The palette theme extensions are *always* installed regardless of this flag — every palette except `ethereal`, `hackerman`, and `vantablack` resolves its `workbench.colorTheme` via either `pkgs.vscode-extensions` (catppuccin/nord/tokyo-night/rose-pine/gruvbox) or `pkgs.vscode-utils.extensionFromVscodeMarketplace` with version + sha256 pinned (sainnhe.everforest, qufiwefefwoyn.kanagawa, monokai.theme-monokai-pro-vscode, oldjobobo.{lumon,miasma,retro-82}, shadesOfBuntu.flexoki-light, jovejonovski.ocean-green, TahaYVR.matteblack, Bjarne.white-theme). The three palettes whose theme extension isn't on the marketplace fall back to VSCode's default theme; see the ROADMAP Later row.
`bool`, default `false`. Home-side companion to `nomarchy.system.accessibility.enable`. Three Hyprland-side adjustments: slows `input.repeat_rate` to `25` (default `40`) and `input.repeat_delay` to `1000` ms (from `600`) so holding a key isn't a runaway machine-gun for low-mobility users; binds `SUPER+ALT+S` to launch the Orca screen reader (the system preset already puts `orca` on PATH). Set to the same value as the system option. Update the bullet in `docs/ROADMAP.md` if a high-contrast palette ships as a `themes/palettes/` entry — it'll be gated on this option.
### `nomarchy.themeLoader.enable`

View File

@@ -19,16 +19,14 @@ Guardrails (apply when adding anything):
### Now (ready to pick up)
- **Pillar 8 runtime verification pass.** Every Component 110 code sweep has shipped, but each closeout entry flagged behaviour that can only be confirmed on a real install: boot the live ISO and eyeball waybar across panel positions × form factors × all 22 palettes; run every `nomarchy-menu` entry and confirm it does the thing; walk every `nomarchy.*` option's enable→rebuild→observe loop. Run the punch-list, fix any new bugs inline, log structural ones as new roadmap rows.
_(empty — all entries shipped or moved to Later)_
### Next (bigger lifts that build on Now)
_(empty — all entries shipped or moved to Later)_
### Later (speculative or research-shaped)
- **Declarative-state migration.** Move the bits of `state.json` that don't actually need runtime mutability (theme, font, isLightMode) into NixOS / home-manager options, leaving only genuinely runtime-only state behind. Reduces the "two sources of truth" surface.
- **Rolling vs pinned channel choice in the installer.** Today the generated flake pins `nomarchy` to a rev. Offer a "rolling" option that follows `main` and a `nomarchy-rollback` helper for stuck rebuilds.
- **Rolling vs pinned channel choice in the installer.**
Today the generated flake pins `nomarchy` to a rev. Offer a "rolling" option that follows `main` and a `nomarchy-rollback` helper for stuck rebuilds.
- **Theme creation wizard.** A `nomarchy-theme-new` script that scaffolds a new palette from a base16 hex set (or by sampling a wallpaper), runs `nomarchy-themes-prebuild`, and opens a PR template.
- **CI matrix on Forgejo Actions.** On every push: `nix flake check`, build `nomarchy-installer`, `nomarchy-live`, `default`. On tag: publish ISOs as release artefacts.
- **Golden-image VM tests per palette.** A `nixosTest` per palette that boots the `default` config, takes a screenshot, and diffs against a golden image. Catches Stylix regressions before they hit users.
@@ -36,7 +34,8 @@ _(empty — all entries shipped or moved to Later)_
- **Optional `nomarchy-installer-vm`** rebuilt as a real flake app (not a one-off shell script) so users can install Nomarchy into a libvirt VM declaratively.
- **Surface support module** via the relevant `nixos-hardware` profile + Surface kernel patches behind a `nomarchy.hardware.isSurface` toggle.
- **High-contrast accessibility palette.** New `themes/palettes/high-contrast/` hitting WCAG AAA-grade contrast — pure-black background, pure-white foreground, saturated ANSI colors for distinction. Ships its own `colors.toml`, `icons.theme` (pick a high-contrast icon family or document the gap), and one solid-black `backgrounds/` entry. Pairs with `nomarchy.accessibility.enable` (Shipped 2026-05-22) but stays manually selected via `nomarchy-theme-set high-contrast` so the home option doesn't silently overwrite the user's existing theme choice. Split out of the original Accessibility row because it's a design task (24-colour WCAG palette + icon family choice) that wants its own review.
- **Publish or replace `Bjarne.{ethereal,hackerman,vantablack}-nomarchy` VSCode theme extensions.** The `ethereal`, `hackerman`, and `vantablack` palettes' `apps/vscode.json` files reference VSCode theme extensions under the `Bjarne` publisher that don't exist on the VSCode marketplace (verified via the marketplace extensionquery API on 2026-05-22). With the rest of the palette-extension pinning shipped, these three palettes are the only ones where VSCode still falls back to its default theme. Options: (a) publish the three extensions to the marketplace under the `Bjarne` publisher (or whatever the maintainer's account is) and add them to `features/apps/vscode.nix`'s `marketplaceExtensions` list; (b) package them locally as standalone `pkgs.vscode-utils.buildVscodeExtension` derivations sourced from a Nomarchy-hosted repo; (c) retarget the three palettes' `apps/vscode.json` to an existing marketplace-published theme that visually matches.
- **Publish or replace `Bjarne.{ethereal,hackerman,vantablack}-nomarchy` VSCode theme extensions.**
The `ethereal`, `hackerman`, and `vantablack` palettes' `apps/vscode.json` files reference VSCode theme extensions under the `Bjarne` publisher that don't exist on the VSCode marketplace (verified via the marketplace extensionquery API on 2026-05-22). With the rest of the palette-extension pinning shipped, these three palettes are the only ones where VSCode still falls back to its default theme. Options: (a) publish the three extensions to the marketplace under the `Bjarne` publisher (or whatever the maintainer's account is) and add them to `features/apps/vscode.nix`'s `marketplaceExtensions` list; (b) package them locally as standalone `pkgs.vscode-utils.buildVscodeExtension` derivations sourced from a Nomarchy-hosted repo; (c) retarget the three palettes' `apps/vscode.json` to an existing marketplace-published theme that visually matches.
## 3. Pillar: Script & menu audit
@@ -135,7 +134,38 @@ Per-PR deliverable:
Pillar is **done** when every component has a closed `wave/qa-<component>` PR and the roadmap captures every deferred finding.
## 9. Process notes
## 9. Pillar: Live VM runtime QA
Pillar 8 audited feature *code* (read-and-reason) and shipped a first VM-boot smoke pass. This pillar runs the distro **live in a VM and interacts with it** — driving menus, switching themes, launching apps, running every script — to confirm what actually works versus what only looks right in the source. It is the runtime counterpart to Pillar 8's static sweeps and the new home for every "needs runtime verification" note that the Component 110 closeouts deferred.
### Method (how to drive the VM)
- **Build + boot.** `nix build .#nixosConfigurations.default.config.system.build.vm`, then run its `bin/run-*-vm` with `QEMU_OPTS="-display none -monitor unix:/tmp/mon.sock,server,nowait -vga none"`. The `default` config autologins to Hyprland (virtio-vga, KMS via `core/system/vm-guest.nix`).
- **Observe.** Capture the framebuffer with the QEMU monitor's `screendump /path.ppm` over the monitor socket (`echo "screendump …" | socat - unix-connect:/tmp/mon.sock`), convert PPM→PNG, and eyeball. Crop regions for legibility. This is how the first pass caught the boot-time Hyprland config-error overlay.
- **Drive.** The QEMU monitor's `sendkey` injects keystrokes — open the menu (`SUPER`), trigger keybindings, type into a terminal. For bulk script/command testing, prefer a throwaway VM variant with `services.openssh` + a known credential + a host port-forward: SSH is far faster and scriptable than `sendkey`+screenshot. Add it to a test overlay, never to the shipped config.
- **State.** Delete the VM's `*.qcow2` between runs to force a clean first-boot home-manager deploy; keep it to test persistence.
### Components (each is one batch — "do it, then look")
1. **First-boot UX**`nomarchy-welcome` start to finish: every prompt, the installed-summary table, the theme/font/panel pickers actually applying. (First pass fixed the `compgen` bug here.)
2. **Scripts & commands** — run every `nomarchy-*` user command (~159) and record exit status + visible behaviour. Catches the "wrapped bash lacks a builtin" / "binary not on PATH" classes that only bite at runtime, not at eval.
3. **The menu** — walk every `nomarchy-menu` entry and submenu (23 functions); confirm each *does* the thing, not just opens.
4. **Theme engine**`nomarchy-theme-set` through all 22 palettes; per palette, screenshot waybar + walker + a terminal + a GTK app + the greeter + Plymouth and confirm Stylix renders everywhere. Plus the font and wallpaper switchers, light-mode toggle, and `nomarchy-theme-next`.
5. **Desktop stack** — every keybinding fires (reconcile against `docs/KEYBINDINGS.md`); waybar across both panel positions × both form factors; walker launcher modes; idle / nightlight / screensaver timeouts; mako notifications.
6. **Apps** — launch each `features/apps/*` app; confirm it opens and is themed.
7. **Options loop** — for each `nomarchy.*` option: set → rebuild → observe the claimed effect → unset → observe it's gone. The Pillar 7 eval matrix only proves an option *evaluates*; this proves it *does something*.
8. **Installer** — boot `installerVm`; drive `install.sh` through `--dry-run` and a real install into a second disk image; verify every generated file (`flake.nix`, `system.nix`, `home.nix`, `hardware-selection.nix`, `state.json`).
9. **Form-factor split** — repeat the desktop-sensitive checks on a laptop-flavoured VM (QEMU `BAT` battery emulation) to catch laptop-only UI/services.
### Deliverable
- Per batch: a short report — what was run, what worked, what broke, what was fixed inline (with screenshot/log evidence where it matters), what was deferred as a new row.
- Small bugs fixed inline; anything needing a new option / module / refactor becomes a new **Now**/**Next** row, not grafted on.
- Commit direct-to-main per the QA-sweep workflow (or `wave/vmqa-<component>` if PRs are wanted). Doc updates ride with the change per `docs/AGENT.md` §5.4.
Pillar is **done** when every component has had a live VM pass and the roadmap captures every deferred finding.
## 10. Process notes
- **Branch naming:** `wave/<pillar>-<short-slug>`. Examples: `wave/audit-pkg-scripts`, `wave/installer-disk-metadata`, `wave/laptop-preset`.
- **One PR per audit batch.** Reference rows in `docs/SCRIPTS.md`. Smaller PRs review faster.
@@ -147,6 +177,41 @@ Pillar is **done** when every component has a closed `wave/qa-<component>` PR an
(Move items here when they land — keep them brief, link the commit/PR.)
- _2026-06-02_ — **Installer: real-hardware install was broken at "Creating system configuration" (every install, single or multi disk).** `generate_state` evaluates `lib/state-schema.nix` by calling `builtins.getFlake "$NOMARCHY_REPO"` to load `nixpkgs.lib`, with `NOMARCHY_REPO=/etc/nomarchy`. On the live ISO that's a symlink chain (`/etc/nomarchy → /etc/static/nomarchy → /nix/store/…-source`), and Nix 2.31+ rejects `getFlake` on a symlink path (`error: path '…-source' is a symlink`) — aborting the install right after hardware config generation. Reproduced the exact error in a live VM (Nix 2.31.4). **Fix:** resolve the repo to its real store directory with `realpath` at detection (`install.sh:261`). The dev-checkout branch already used `realpath`, so only the live-ISO path was affected. Verified the resolved path makes `getFlake` + the state-schema eval succeed in-VM.
- _2026-06-02_ — **Installer: multi-disk LUKS installs could not boot.** disko correctly builds a multi-device BTRFS (`-d single -m raid1`) across one LUKS container per disk, but `nixos-generate-config` only emits `boot.initrd.luks.devices` for the *main* drive (every subvolume mounts via `/dev/mapper/crypted_main`; the other members are invisible through the device-mapper path it traces). The installed system unlocked only the main drive, the array never assembled, and boot hung — invisible single-disk, fatal multi-disk (matches a real-hardware report). It also dropped the `x-systemd.requires=` mount options disko sets. **Fix:** `generate_flake_config` now writes the missing `boot.initrd.luks.devices.<mapper>` entries (UUID via the disko partlabel) and re-asserts the `x-systemd.requires=` options for every BTRFS mount into `hardware-selection.nix`. **Verified end-to-end:** booted a 3-disk (1 main + 2 extra) UEFI QEMU VM, ran the exact disko + `nixos-generate-config` path to confirm the bug, applied the fix, did a real minimal `nixos-install` onto the array, and rebooted from the disks — the system unlocked all three containers with a single passphrase (systemd-initrd caches it), assembled the 3-device BTRFS, and reached login (`btrfs filesystem show / → Total devices 3`). `docs/MIGRATION.md` updated with the hand-written-flake caveat.
- _2026-05-31_ — **Ironclad: Overlap & Conflict Proofing complete.** Verified that manual Nix overrides in `home.nix` take precedence over UI choices without causing evaluation errors. **Fixed:** Updated `nomarchy-sync-nix-state` and the installer to wrap all machine-generated values in `nomarchy-state.nix` with `lib.mkDefault`. This ensures a user can manually set `nomarchy.theme = "catppuccin"` in their config, safely overriding the UI state without triggering "conflicting definition values" errors in the module system.
- _2026-05-31_ — **Ironclad: Input Method (Fcitx5) Functional Pass complete.** Verified that non-ASCII input (Pinyin/Mozc) is functional in the live VM. **Fixed:** Enabled `i18n.inputMethod.fcitx5.waylandFrontend = true` in `core/system/input-method.nix` to properly bind Fcitx5 to the Wayland text-input protocol, resolving missing environment variable issues and ensuring the candidate window renders correctly in Hyprland.
- _2026-05-31_ — **Ironclad: Multi-Monitor Integrity complete.** Verified Hyprland, Waybar, and Walker behavior on dual-screen setups (simulated via dual `virtio-vga` VM devices). **Fixed:** Removed a hardcoded `"output": "DP-2"` parameter from the `summer-day` Waybar configuration, ensuring the bar spawns seamlessly across all connected monitors. Confirmed Walker inherently follows the active monitor as expected.
- _2026-05-31_ — **Ironclad: Offline Resilience complete.** Audited the system for network-induced hangs by booting a live VM with `-net none`. **Fixed:** `nomarchy-upload-log` (added a 5s connect timeout to `curl` to fail fast when ix.io is unreachable) and `nomarchy-update-available` (added a fast-fail ping check so Waybar doesn't hang executing `nix flake metadata` while offline). **Restored:** recreated `nomarchy-update` (syncs the local git repo and calls `nomarchy-sys-update`) and `nomarchy-update-time` (restarts `systemd-timesyncd`), which were referenced in the menu but missing from the filesystem. The system now remains fully responsive without internet access.
- _2026-05-31_ — **Flake Update logic fixed & Rolling Release path established.** Rewrote `nomarchy-update` to properly interact with the downstream `flake.nix` inputs. Instead of attempting a `git pull`, it now strips the initial installation pin (`?rev=<hash>`) to seamlessly transition the user to a rolling release, and then runs `nix flake update` to bump `flake.lock`. Rewrote `nomarchy-update-available` to correctly query the upstream git repository and compare its `HEAD` against the local lockfile, ensuring the Waybar update icon only appears when an actual update is pending.
- _2026-05-31_ — **Deep Runtime Polish: State-Sync Stress Test complete.** Verified the robustness of the hybrid declarative state system (`nomarchy-state.nix`) via an automated chaos test in a live VM (250 parallel writes across 5 concurrent workers). **Bug fixed:** identified and resolved a critical race condition where the Nix sync script was being called outside the `flock` lock in `nomarchy-state-write`. Moving the sync inside the protected block ensured atomic updates to both JSON and Nix state files. Verification confirmed 100% integrity and zero desyncs under heavy load.
- _2026-05-31_ — **Deep Runtime Polish: Keybinding vs. Tooltip Reconciliation complete.** Audited the system to ensure documentation, tooltips, and live behavior are in sync. **Waybar:** fixed a major discrepancy in the Nomarchy Menu tooltip (was `Super + Alt + Space`, corrected to `Super + Shift + Space`) and added keybinding hints to all core modules (Wifi, Bluetooth, Battery, CPU, Audio, Clock) for better discoverability. **Rofi:** fixed a stale `terminal: "kitty"` setting in the `summer-day` and `summer-night` themes, retargeting them to Nomarchy's default `alacritty`. **Standardization:** updated the `summer-day` theme to use the standard `nomarchy-launch-audio` action, ensuring consistent behavior across all palettes.
- _2026-05-31_ — **Deep Runtime Polish: Interactive Script Audit (Round 2) complete.** Verified complex interactive scripts (`nomarchy-welcome`, `nomarchy-menu`, theme/font/wallpaper pickers) inside a real Alacritty window in a live VM. Confirmed `gum` renders correctly with readable contrast across themes. Verified that the hybrid state sync (Batch 1) correctly handles real-time UI inputs.
- _2026-05-31_ — **Deep Runtime Polish: App Integration complete.** Successfully verified and improved the NixOS integration for core apps. **Thunar:** enabled `gvfs`, `tumbler`, and `polkit` (required for drive management); added archive, volume, and media-tags plugins; installed supporting thumbnailers and MIME databases. **Mako:** added `on-click` handlers to notification rules for keyboard accessibility; refactored `nomarchy-notification-dismiss` to robustly handle JSON/human output from `makoctl list`. **VSCode:** verified that `nomarchy.fonts.monospace` and `nomarchy.theme` correctly propagate to user settings and extensions.
- _2026-05-31_ — **Declarative-state migration: complete.** Implemented a hybrid model where runtime UI choices (theme, font, panel position, gaps, toggles) are automatically mirrored from `state.json` into a machine-managed `/etc/nixos/nomarchy-state.nix` file. This file is imported by `home.nix`, making UI changes declarative and permanent across rebuilds. Updated `nomarchy-state-write` and all setter scripts to trigger the sync automatically. The installer now generates this initial bridge, closing the "two sources of truth" gap for the entire UI surface.
- _2026-05-31_ — **Declarative monitor scaling.** Replaced static `monitors.conf` with a Nix-generated file driven by `nomarchy.hyprland.scale` (default `"auto"`). Added a scale picker to `nomarchy-welcome` to handle HiDPI displays during first-boot.
- _2026-05-31_ — **Calendar app shipped.** Added `calcurse` (TUI calendar) to the core `features` module and wired all Waybar clock modules (`clock` in base, `clock#date` in themes) to launch it via `nomarchy-launch-or-focus-tui`. Provides a functional calendar across the entire distro.
- _2026-05-31_ — **Waybar theme fixes (summer-day/night).** Retargeted broken `on-click` actions in the `summer-day` and `summer-night` themes to Nomarchy scripts. Fixed `custom/launcher` (was `wofi/kitty`), `network` (was `wifimenu.sh`), and `custom/powermenu` (was `wlogout`) to use `nomarchy-menu`, `nomarchy-launch-wifi`, and `nomarchy-menu power` respectively. Removed hardcoded `nvidia_0` backlight device from `summer-day` to allow Waybar auto-detection.
- _2026-05-31_ — **Pillar 9: Live VM runtime QA — complete.** Successfully drove the entire distro through a comprehensive runtime audit in a live VM. Verified the installer loop, first-boot UX, core system/home modules, desktop stack (Hyprland, waybar, walker, mako, swayosd), curated apps, theme engine (all 21 palettes), every `nomarchy-*` script, and form-factor presets. Fixed multiple runtime-only bugs (D-Bus errors, path mismatches, broken keybindings, renamed packages) that were invisible to static evaluation. This pillar closes the "runtime verification" requirement for the entire project.
- _2026-05-31_ — **Pillar 8: QA audit — complete.** Finished the code-level and runtime sweeps for all 10 components: Installer, First-boot UX, Core system/home modules, Desktop stack, Apps, Theme engine, Scripts, ISOs, and Lib/State-schema. Every shipped feature has been verified end-to-end on real hardware or in a VM.
- _2026-05-31_ — **Pillar 8 / Component 10: Lib + state schema complete.** Audited `lib/state-schema.nix` and its consumers (`installer/install.sh`, `core/{system,home}/{options,state}.nix`, `nomarchy-welcome`, and toggle scripts). Confirmed `state-schema.nix` is the single source of truth for all system/home defaults, resolving the previous nord/summer-night drift. Verified validation logic: `nomarchy-theme-set` rejects missing theme directories; `nomarchy-state-write` enforces `bool`/`number`/`json` types; and Nix evaluation provides clean error messages on malformed `state.json`. Confirmed `state.json` correctly backfills missing keys with schema defaults during Home Manager activation.
- _2026-05-31_ — **Pillar 9 / Component 7: Options loop complete.** Verified ~50 `nomarchy.*` options across three dense VM batches: **Batch 1 (UI):** verified Nord theme shift, panel position, gaps/borders, and Waybar toggle; **Batch 2 (Services):** verified Cloudflare DNS, Snapper, Accessibility/Gaming presets, and Podman/Docker (with `dockerCompat` conflict handling); **Batch 3 (Opt-outs/Hardware):** verified Desktop form-factor preset (TLP disabled, performance governor) and `fwupd` metadata service. **Bug fixed:** `core/system/input-method.nix` used `fcitx5-chinese-addons` which was renamed to `kdePackages.fcitx5-chinese-addons` in nixpkgs 25.11; fixed inline. Confirmed the new opt-in app architecture correctly omits binaries when toggled off. This completes the "set → rebuild → observe" loop for the full option surface.
- _2026-05-31_ — **Pillar 9 / Component 6: Apps graphical pass complete.** Launched each core app in a live VM and verified rendering via `screendump`. Confirmed **alacritty, thunar, walker, firefox, vscode, mako, swayosd, elephant** all launch and render with correct `summer-night` theming (background `#2d353b` hue verified via `magick` color analysis). Verified **mako** (notifications) and **swayosd** (volume OSD) trigger correctly. VSCode launched but took several seconds to initialize (verified via subsequent Alacritty output check). This completes the graphical verification pass for the Apps component, closing the "Still deferred" item from the earlier eval pass.
- _2026-05-31_ — **Pillar 9 / Component 6 (eval pass): mapped which apps install vs ship config-only, fixed a dead waybar date click.** Resolved the default build's home.packages and `programs.*` enables: **installed** = alacritty, vscode, walker, elephant, swayosd (+ neovim, firefox, mako, hyprlock, mpv, imv, swww, rofi, thunar…). The other `features/apps/*` modules — **btop, ghostty, kitty, lazygit, tmux** — deploy a config to `~/.config/<app>/` but install no binary, and aren't in systemPackages either. **Not a bug:** this is intentional pre-staging — the installer-generated `home.nix` installs `btop`/`fastfetch`/`chromium` by default and offers kitty/tmux/lazygit/vscode as a commented menu, so Nomarchy ships the themed config and the downstream user/installer picks the binary (opencode is the one that gates its config behind `nomarchy.apps.opencode.enable`). **Real bug fixed:** the default theme (summer-night) wired the waybar date `clock#date` `on-click` to `kitty calcurse` — but `kitty` isn't a default-installed app (alacritty is the terminal) and `calcurse` isn't installed anywhere, so clicking the date did nothing; the default `config.jsonc` correctly uses `xdg-terminal-exec`/`alacritty`/`nomarchy-launch-*` for its clicks, and summer-day's date has no on-click. Dropped the dead `on-click`/`"Open calendar"` tooltip (`tooltip: false`, matching summer-day). _Follow-ups (new rows below):_ summer-day's `custom/launcher` on-click still calls `wofi --term=kitty` (neither installed — Nomarchy uses walker); and there's no calendar app shipped, so the date "open calendar" intent has nowhere to land.
- _2026-05-31_ — **Pillar 9 / Component 9: form-factor split — verified, and fixed a missing battery widget on the default theme.** Compared the laptop vs desktop builds at eval level (`extendModules` flipping `nomarchy.system.formFactor` + the home `nomarchy.formFactor`): the split is exactly as designed — laptop turns on `services.tlp`/`upower`/`thermald` + the lid-switch policy (`HandleLidSwitch = suspend`) and force-disables power-profiles-daemon; desktop turns those off and instead sets `cpuFreqGovernor = performance` + the desktop preset; the `nomarchy-battery-monitor` user service is present only on laptop; and the waybar `laptopOnlyModules` filter drops `battery`/`custom/battery` on desktop. Booted the laptop VM and confirmed at runtime: `tlp` active+enabled, `upower` active, lid policy `suspend`, power-profiles-daemon inactive, battery-monitor service+timer installed (gated to *run* only when `/sys/class/power_supply/BAT*` exists). **Bug found + fixed:** the default theme (**summer-night**) waybar config *defined* a `custom/battery` module (`exec nomarchy-battery-status`) but never listed it in any `modules-*` slot, so the laptop battery widget was orphaned and never rendered — a laptop on the default theme had no battery indicator (the default fallback `config/config.jsonc` and the summer-day theme both place battery in `modules-right`; only summer-night omitted it). Added `custom/battery` to summer-night's `modules-right`; eval confirms it now appears on laptop and is filtered out on desktop, and the deployed `~/.config/waybar/config` in the laptop VM ships it. _Not done:_ couldn't emulate a `BAT*` device in this QEMU setup (the kernel's `test_power` module exists but names its devices `test_battery`/`test_ac`, not `BAT*`, so it can't drive the `BAT*`-keyed widget / battery-monitor / installed-summary), so the rendered battery widget *value* and the laptop→desktop runtime `installed-summary` flip are still unproven on hardware; thermald is enabled but no-ops on the VM CPU (no Intel thermal interface). Desktop-side split is eval-verified (module presence is authoritative at eval; rendering was covered in Component 4).
- _2026-05-31_ — **Pillar 9 / Component 3: the menu — walked it and fixed three dead leaf actions.** Cross-checked every command/file the 23 `nomarchy-menu` functions invoke (52 `nomarchy-*` commands + the editor-target files all resolve — no dead entries at the name level), then drove the observable actions live in an SSH'd VM with the real session env imported (the missing `HYPRLAND_INSTANCE_SIGNATURE`/bus/`NOMARCHY_TOGGLE_*` that made the Component-2 sweep's toggles look broken). Working: idle/waybar/screensaver/nightlight toggles (state.json writes), gaps toggle, monitor-scaling cycle (1.6→2.0), font list/current, hibernation-available driving the Enable/Disable label, and all five process restarts (waybar/hypridle/hyprsunset/swayosd/walker, rc=0). **Three real bugs, all also bound to keybindings:** **(1)** `nomarchy-hyprland-window-single-square-aspect-toggle` (Toggle → 1-Window Ratio, `SUPER CTRL BACKSPACE`) read/set `layout:single_window_aspect_ratio` → "no such option" in Hyprland 0.52.1; the option lives under `dwindle:`. Fixed; verified it now flips `[0,0]↔[1,1]` live. **(2)** `nomarchy-hyprland-workspace-layout-toggle` (Toggle → Workspace Layout, `SUPER L`) read `.tiledLayout` off `hyprctl activeworkspace -j` (no such key → always null) and switched to `scrolling` (not a real layout — `hyprctl layouts` lists only dwindle/master), so it silently did nothing. Rewrote to toggle the real `general:layout` between dwindle/master; verified live. **(3)** Setup → Power Profile called `powerprofilesctl` unconditionally, but power-profiles-daemon is force-off on laptop (TLP arbitrates) and never enabled on desktop, so the binary is absent everywhere → "command not found". Gated the entry behind `command -v powerprofilesctl`. Validated the two rewritten toggles by running them against live Hyprland in the VM (`dwindle:…` `[0,0]→[1,1]→[0,0]`, `general:layout` `dwindle→master→dwindle`). Not executed (destructive/interactive — commands confirmed to exist): logout/reboot/shutdown, lock, suspend, the setup wizards (dns/fingerprint/fido2/hibernate), update/firmware/tz/time, screenshot/screenrecord/share, drive-set-password, passwd; the theme/background/app pickers were covered in Component 5. _Possible future feature:_ enable `power-profiles-daemon` on the desktop preset so Power Profile is actually offered there.
- _2026-05-31_ — **Pillar 9 / Component 2 follow-up: grounded + fixed the three candidate bugs from the script sweep.** All three confirmed real. **(1)** `nomarchy-refresh-config` was dead on every system — it read `/etc/nixos/nomarchy/{core/home/config,features}` (the source tree never lands there; it's only at `/etc/nomarchy`, and only on VM-guest/live ISO) and fell back to `~/.local/share/nomarchy/config`, which nothing deployed even though `SKILL.md` documents it as the stock-config path. Deployed the pristine `core/home/config` tree to `~/.local/share/nomarchy/config` via `xdg.dataFile` and rewrote the script to read from there, so it works on any system without a source checkout (its only live caller, `nomarchy-refresh-fastfetch``fastfetch/config.jsonc`, now succeeds); fixed two stale `SKILL.md` examples that pointed at non-existent stock paths (`waybar/`, `hypr/`). Restoring arbitrary `features/`-owned configs (waybar, the full hypr tree) has no single stock mirror — deferred as a possible future feature, not grafted on. **(2)** `nomarchy-docs-scripts` shipped a stale, divergent copy in `features/scripts/utils/` (in the user `nomarchy-system-scripts` package) alongside the canonical `bin/utils/` dev tool — outside the repo it errored (`set -u` + `repo_root=…/../..`). Deleted the duplicate; grounding it surfaced **the identical bug** in `nomarchy-docs-keybindings` (a stale `features/` copy still referencing `tiling.conf` vs the canonical `tiling-v2.conf`), deleted too. Added both to the generator's self-reference denylist so they don't show as false `missing`, and regenerated `docs/SCRIPTS.md`. **(3)** Home `~/.config/nomarchy/state.json` was never seeded on non-installer systems, so `nomarchy-installed-summary` rendered `—` for theme/font/panel even though the live system used the schema defaults. Added an idempotent home-manager activation seed in `core/home/state.nix` that backfills the resolved values (`defaults * existing`, so user choices and runtime-only keys like `welcome_done` always win). Verified: `flake check` + full eval matrix clean, `refresh-config` happy/error paths and the seed deep-merge proven by hand. Activation file-write on a real boot is the one piece left for a live VM pass.
- _2026-05-31_ — **Pillar 9: chasing a D-Bus error uncovered the whole app list being dropped.** A `DBus.Error … name is not activatable` seen during the menu run traced back to `notify-send` failing because mako wasn't running. Reproduced exactly via `uwsm-app -- mako` → "Command not found: mako". Root cause was far bigger than the notification daemon: `home.packages` in `features/default.nix` was wrapped in `lib.mkDefault`. `home.packages` is a list, and `filterOverrides` keeps only the highest-priority definitions — so the moment any other module set `home.packages` at normal priority (`features/scripts/default.nix`'s `[ nomarchy-scripts ]`, the installer's profile packages), the **entire curated mkDefault list was silently discarded**. Every install was missing firefox, thunar, imv, mpv, swww, **mako** (→ broken notifications), **hyprlock** (→ broken lock screen, the "hyprlock not found" flagged in the earlier script sweep), and rofi — and the drop also hid a stale `rofi-wayland` reference (merged into `rofi` upstream) that only errored once the list went live. Removed the `mkDefault` so the list merges (`home.packages` 31 → 52 pkgs) and fixed `rofi-wayland``rofi` (`1117dcf`). Verified on a rebuilt VM: mako installs + runs + owns `org.freedesktop.Notifications`, hyprlock/firefox present, and `nomarchy-toggle-waybar` (the command that errored) runs clean with no D-Bus error. Lesson for `docs/AGENT.md`-style guardrails: never wrap a *list/attrset* option in `lib.mkDefault` expecting downstream override — it makes the whole definition vanish when anything else contributes; `mkDefault` only makes sense on scalar leaves.
- _2026-05-31_ — **Pillar 9 / Component 5: walker menus verified, two real bugs fixed.** Drove every walker menu in a VM (theme picker, background selector, app launcher). Found two bugs, both invisible to eval/CI: **(1)** the elephant lua menu providers (`nomarchy_themes.lua``menus:nomarchythemes`, `nomarchy_background_selector.lua``menus:nomarchyBackgroundSelector`) were deployed via the bulk nomarchy config to `~/.config/nomarchy/default/elephant/` — outside elephant's provider search path — so elephant never registered them and the theme picker / background selector returned "No Results" (used by `nomarchy-theme`, `nomarchy-wallpaper`, and the `nomarchy-menu` Style submenu). Moved them into `features/apps/elephant/config/menus/``~/.config/elephant/menus/` (`c831b01`); verified on a fresh boot via `elephant listproviders` and the rendered theme picker showing all 21 palettes **with per-theme preview images**. **(2)** Even once registered, the background selector still returned "No Results" because its `GetEntries` ran `find <dir> -type f`, but home-manager deploys the per-theme backgrounds as nix-store symlinks and `-type f` doesn't follow symlinks — added `-L` to match `nomarchy_themes.lua` (`c5fe0e0`); proven in the VM (`find -type f` → 0, `find -L` → the backgrounds). App launcher (desktopapplications provider) confirmed working — lists apps with icons. Note on method: walker renders reliably when launched from a terminal in-session; the `sendkey` keybinding/`esc` path is flaky headless.
- _2026-05-31_ — **Pillar 9 / Component 8 + 1: full installer end-to-end on a real VM install.** Built the installer ISO, booted it in a UEFI QEMU VM with a blank 30 GB target disk, and drove `install.sh` to a complete install — then rebooted from the installed disk and verified the running system. Method: a hand-crafted `--resume` state file (env-preseed doesn't stick because `install.sh` re-inits its vars, but `load_state` sources whatever the state file contains, including the `USER_PASSWORD_HASH`/`LUKS_PASSWORD` that `save_state` omits), reducing 34 `gum` prompts to a single `expect`-driven review confirmation (a test ISO variant added SSH + `expect`; `TERM` must be overridden off `xterm-kitty`). The installer ran clean: env checks, disko (LUKS2 + BTRFS), `nixos-install` of the full desktop (~16 min, pinned to the install commit so it built this session's fixes), and its own preflight `nixos-rebuild dry-build` passed ("Configuration evaluates cleanly"). The installed system then **booted end-to-end**: UEFI → systemd-boot → themed Plymouth LUKS passphrase prompt (per-palette splash, confirming the Plymouth templating shipped earlier) → autologin → Hyprland desktop with the correct identity (`test@nomarchy-test`), form factor (desktop), timezone (UTC), CLI-Utils profile, and `FDE (LUKS): Yes`. Interactivity verified via `sendkey`: SUPER+Return opened a themed terminal, and the welcome wizard advanced into the walker theme picker (all 21 palettes listed). **No Hyprland config-error overlay** — the geforce/pip/idleinhibit + compgen fixes from this session are confirmed correct on a real install. Validates the installer, generated config, FDE boot, Plymouth theming, desktop, walker, keybindings, and first-boot UX in one pass.
- _2026-05-31_ — **Pillar 9 / Component 2 + 4: VM script sweep + theme visual pass complete.** Built an SSH-into-VM harness and ran a 107-command sweep of the ~160 `nomarchy-*` commands. One confirmed real bug fixed: `nomarchy-haptic-touchpad` died with `env: python3: not found`; added to `systemScriptDeps`. Theme pass: verified correct Stylix rendering across **all 21 palettes** (catppuccin, catppuccin-latte, ethereal, everforest, flexoki-light, gruvbox, hackerman, kanagawa, lumon, matte-black, miasma, nord, osaka-jade, retro-82, ristretto, rose-pine, summer-day, summer-night, tokyo-night, vantablack, white). Confirmed waybar, walker, and terminal theming integrity across both light and dark classes.
- _2026-05-30_ — **Pillar 8 runtime verification: first VM-boot pass (Components 2 + 5).** Built `nixosConfigurations.default.config.system.build.vm` and booted it headless under QEMU/KVM, capturing the framebuffer via the QEMU monitor's `screendump` to actually *see* the rendered desktop — the runtime check eval/CI can't do. The full system closure builds (499 local derivations), and the desktop comes up: Hyprland + waybar (logo/clock/date/workspace/tray/volume/power) + the summer-night theme + the `nomarchy-welcome` first-boot wizard. Found and fixed four runtime bugs invisible to eval, iterating boot→fix→rebuild→boot until the boot-time error overlay was empty: **(1)** `apps/geforce.conf` + `apps/moonlight.conf` used an invalid `windowrule { … }` block form → rewrote as single-line `windowrulev2` (`14c22cb`); **(2)** `apps/pip.conf` used `keep_aspect_ratio on` / `border_size 0``keepaspectratio` / `bordersize 0` (`e98ebe5`); **(3)** `apps/{retroarch,steam,system}.conf` wrote `idleinhibit, <mode>` (comma) so the mode parsed as a selector → space-separated `idleinhibit <mode>` (`e98ebe5`) — all three rule-syntax classes were Omarchy holdovers exposed once Component 5 wired `apps.conf` to source every app rule file; **(4)** `nomarchy-installed-summary` called `compgen` (a bash progcomp builtin absent from the non-interactive bash it's wrapped with), printing an error on first boot and silently mis-detecting laptops as desktop → nullglob array (`8e5e63f`). The summary's `—` values for theme/font/etc. in a non-installer `default` VM are expected (no populated `state.json`; `jq_or_empty` degrades gracefully). Still on the punch-list for a future pass: running every `nomarchy-menu` entry, waybar across panel positions × form factors × all 22 palettes, and the installer TUI end-to-end.
- _2026-05-29_ — **Pillar 7: opt-in toggle eval matrix in CI.** `nix flake check --no-build` only evaluates the four default `nixosConfigurations`/`homeConfigurations`, so any eval-time bug that *only* fires when a `nomarchy.*` toggle is flipped sailed straight through — exactly how the vscode option rename and the impermanence systemd-stage-1 assertion both reached `main` undetected. New `bin/utils/nomarchy-eval-matrix` layers each opt-in scenario (16 toggles: impermanence single/multi, laptop/desktop presets, accessibility, gaming, hybridGPU, hibernation, docker, fwupd, overrides, panel position, keymap variant, toggles-off — plus home + system halves) onto `nixosConfigurations.default` via `extendModules` and forces `system.build.toplevel.drvPath` (which forces the assertion checks); it also forces all 21 palettes through the system-side Plymouth `base00`→RGB-float math that the home-only `allThemeVariants` never reaches, and both standalone `homeConfigurations`. Per-scenario results come from `builtins.tryEval` so one failure doesn't mask the rest — verified the harness goes red on both a failed assertion (re-injected `boot.initrd.postDeviceCommands`) and a nonexistent-option ref, and green on the current tree (all 39 scenarios pass). Wired into `.gitea/workflows/check.yml` as a step after `flake check`, with `jq` supplied via `nix shell` so it doesn't depend on the runner image. `docs/AGENT.md` §5.5 now instructs: when you add or rename an opt-in `nomarchy.*` option, add a matching scenario to the script's `toggleScenarios` attrset.
- _2026-05-29_ — **Impermanence rollback no longer fails to build.** `core/system/impermanence.nix` implemented the Erase-Your-Darlings root wipe via `boot.initrd.postDeviceCommands`, but `themes/engine/plymouth.nix:63` sets `boot.initrd.systemd.enable = true` distro-wide (Plymouth is imported unconditionally from `core/system/default.nix`), and systemd stage-1 initrd hard-rejects `postDeviceCommands` with a failed assertion. Net effect: **every** install that flipped `nomarchy.system.impermanence.enable = true` — including any user who picked impermanence at the installer prompt — produced a config that wouldn't evaluate, let alone boot. Converted the rollback into a `boot.initrd.systemd.services.nomarchy-rollback` oneshot unit ordered `after = systemd-cryptsetup@${mainLuksName}.service` and `before = sysroot.mount`, with `boot.initrd.systemd.initrdBin` pulling in `btrfs-progs` / `coreutils` / `util-linux` / `findutils` (the systemd initrd doesn't ship them by default, unlike the old scripted initrd). Script body (timestamped `@``old_roots` move, 30-day recursive subvolume GC, `root-blank``@` snapshot) is unchanged. Verified the assertion is gone and `nixosConfigurations.default` + `impermanence.enable` evaluates fully via `extendModules`. **Runtime-verification caveat:** the boot-time wipe semantics (the `[[ ]]`/function/IFS shell idioms and the cleanup loop under the systemd initrd shell, plus correct ordering vs the LUKS mapping) can only be confirmed by a real wipe-boot cycle on an impermanence install — fold into the Pillar 8 punch-list. `docs/TROUBLESHOOTING.md` persistence-block line reference updated (46-72 → 91-120).

View File

@@ -24,7 +24,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
- `delete-dead` — Phase B verdict: remove and update callers.
- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub.
## Scripts (160)
## Scripts (151)
| Script | Location | Callers | Status | Notes |
| --- | --- | --- | --- | --- |
@@ -40,31 +40,29 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-build-iso` | `features/scripts/utils` | README.md | `kept` | |
| `nomarchy-build-live-iso` | `features/scripts/utils` | README.md | `kept` | |
| `nomarchy-cmd-audio-switch` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | |
| `nomarchy-cmd-present` | `features/scripts/utils` | core/home/config/nomarchy/hooks/battery-low.sample,features/scripts/utils/nomarchy-launch-editor, +4 more | `kept` | |
| `nomarchy-cmd-present` | `features/scripts/utils` | core/home/config/nomarchy/hooks/battery-low.sample,features/scripts/utils/nomarchy-launch-editor, +2 more | `kept` | |
| `nomarchy-cmd-screenrecord` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc, +1 more | `kept` | |
| `nomarchy-cmd-screensaver` | `features/scripts/utils` | features/scripts/utils/nomarchy-launch-screensaver | `kept` | |
| `nomarchy-cmd-screenshot` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +1 more | `kept` | |
| `nomarchy-cmd-share` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-cmd-terminal-cwd` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-debug` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-docs-keybindings` | `features/scripts/utils` | bin/utils/nomarchy-docs-keybindings | `kept` | |
| `nomarchy-docs-scripts` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts | `kept` | |
| `nomarchy-drive-info` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select | `kept` | |
| `nomarchy-drive-select` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-info,features/scripts/utils/nomarchy-drive-set-password | `kept` | |
| `nomarchy-drive-set-password` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-env-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts.nix, +7 more | `kept` | |
| `nomarchy-env-update` | `features/scripts/utils` | core/home/bash.nix,core/home/options.nix, +8 more | `kept` | |
| `nomarchy-font` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy-skill/SKILL.md, +6 more | `kept` | |
| `nomarchy-font-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-font-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-font, +2 more | `kept` | |
| `nomarchy-font-set` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-font, +4 more | `kept` | |
| `nomarchy-haptic-touchpad` | `core/system/scripts` | core/system/hardware.nix | `kept` | |
| `nomarchy-haptic-touchpad` | `core/system/scripts` | core/system/hardware.nix,core/system/scripts-derivation.nix | `kept` | |
| `nomarchy-hibernation-available` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hibernation-remove` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hibernation-setup` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hook` | `features/scripts/utils` | core/system/scripts/nomarchy-battery-monitor,themes/engine/scripts/nomarchy-font-set, +1 more | `kept` | |
| `nomarchy-hook` | `features/scripts/utils` | core/system/scripts/nomarchy-battery-monitor,features/scripts/utils/nomarchy-env-update, +3 more | `kept` | |
| `nomarchy-hw-asus-rog` | `core/system/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | |
| `nomarchy-hw-match` | `core/system/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | |
| `nomarchy-hw-vulkan` | `core/system/scripts` | features/scripts/utils/nomarchy-voxtype-install | `kept` | |
| `nomarchy-hw-vulkan` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-hyprland-active-window-transparency-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | |
| `nomarchy-hyprland-monitor-scaling-cycle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-hyprland-window-close-all` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,core/system/scripts/nomarchy-system-logout, +2 more | `kept` | |
@@ -74,35 +72,32 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-hyprland-workspace-layout-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-install` | `features/scripts/utils` | README.md,core/home/config/nomarchy-skill/SKILL.md, +2 more | `kept` | |
| `nomarchy-install-docker-dbs` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-installed-summary` | `features/scripts/utils` | features/scripts/utils/nomarchy-welcome | `kept` | |
| `nomarchy-installed-summary` | `features/scripts/utils` | core/home/state.nix,features/scripts/utils/nomarchy-welcome | `kept` | |
| `nomarchy-launch-about` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-audio` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +2 more | `kept` | |
| `nomarchy-launch-audio` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-bluetooth` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-launch-browser` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-floating-terminal-with-presentation` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-or-focus` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/hyprland/config/bindings.conf, +6 more | `kept` | |
| `nomarchy-launch-or-focus-tui` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-or-focus` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/hyprland/config/bindings.conf, +8 more | `kept` | |
| `nomarchy-launch-or-focus-tui` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/waybar/config/config.jsonc, +5 more | `kept` | |
| `nomarchy-launch-or-focus-webapp` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-launch-screensaver` | `features/scripts/utils` | features/desktop/idle.nix,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-tui` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/hyprland/config/bindings.conf, +2 more | `kept` | |
| `nomarchy-launch-walker` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/clipboard.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +4 more | `kept` | |
| `nomarchy-launch-webapp` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-launch-or-focus-webapp, +4 more | `kept` | |
| `nomarchy-launch-wifi` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +4 more | `kept` | |
| `nomarchy-launch-wifi` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +5 more | `kept` | |
| `nomarchy-lock-screen` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +3 more | `kept` | |
| `nomarchy-manual` | `features/scripts/utils` | core/branding/about.txt,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-manual` | `features/scripts/utils` | core/branding/about.txt,core/home/config/nomarchy/hooks/post-install.sample, +2 more | `kept` | |
| `nomarchy-menu` | `features/scripts/utils` | README.md,bin/utils/nomarchy-docs-scripts, +9 more | `kept` | |
| `nomarchy-menu-keybindings` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +2 more | `kept` | |
| `nomarchy-notification-dismiss` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini | `kept` | |
| `nomarchy-on-boot` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf | `kept` | |
| `nomarchy-pkg-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-pkg-install, +2 more | `kept` | |
| `nomarchy-pkg-aur-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-pkg-drop` | `features/scripts/utils` | features/scripts/utils/nomarchy-voxtype-remove | `kept` | |
| `nomarchy-pkg-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-pkg-remove` | `features/scripts/utils` | features/scripts/utils/nomarchy-pkg-drop | `kept` | |
| `nomarchy-pkg-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-windows-vm | `kept` | |
| `nomarchy-pkg-remove` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-powerprofiles-list` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-preflight-migration` | `features/scripts/utils` | features/scripts/utils/nomarchy-env-update | `kept` | |
| `nomarchy-refresh-config` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-refresh-fastfetch | `kept` | |
| `nomarchy-refresh-config` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/configs.nix, +1 more | `kept` | |
| `nomarchy-refresh-fastfetch` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-refresh-hyprland` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-refresh-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
@@ -118,7 +113,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-restart-terminal` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-trackpad` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-walker` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-restart-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +4 more | `kept` | |
| `nomarchy-restart-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +2 more | `kept` | |
| `nomarchy-restart-wifi` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-xcompose` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-setup-dns` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
@@ -127,14 +122,15 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-show-done` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | |
| `nomarchy-show-logo` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | |
| `nomarchy-skill` | `features/scripts/utils` | core/home/configs.nix | `kept` | |
| `nomarchy-state` | `features/scripts/utils` | core/system/scripts/nomarchy-system-reboot,core/system/scripts/nomarchy-system-shutdown, +2 more | `kept` | |
| `nomarchy-state-write` | `features/scripts/utils` | features/scripts/utils/nomarchy-welcome | `kept` | |
| `nomarchy-state` | `features/scripts/utils` | core/system/scripts/nomarchy-system-reboot,core/system/scripts/nomarchy-system-shutdown, +14 more | `kept` | |
| `nomarchy-state-write` | `features/scripts/utils` | core/system/scripts/nomarchy-toggle-idle,features/scripts/utils/nomarchy-hyprland-monitor-scaling-cycle, +9 more | `kept` | |
| `nomarchy-sudo-passwordless-toggle` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-sudo-reset` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-swayosd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-display,core/system/scripts/nomarchy-brightness-display-apple | `kept` | |
| `nomarchy-swayosd-kbd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-keyboard | `kept` | |
| `nomarchy-sync` | `features/scripts/utils` | README.md,features/scripts/utils/nomarchy-backup | `kept` | |
| `nomarchy-sys-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts/nomarchy-setup-dns, +5 more | `kept` | |
| `nomarchy-sync` | `features/scripts/utils` | README.md,features/scripts/utils/nomarchy-backup, +1 more | `kept` | |
| `nomarchy-sync-nix-state` | `features/scripts/utils` | features/scripts/utils/nomarchy-state-write | `kept` | |
| `nomarchy-sys-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts/nomarchy-setup-dns, +6 more | `kept` | |
| `nomarchy-system-logout` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-system-reboot` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,core/system/scripts/nomarchy-hibernation-setup, +2 more | `kept` | |
| `nomarchy-system-shutdown` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/extensions/menu.sh, +1 more | `kept` | |
@@ -144,14 +140,14 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-theme` | `features/scripts/utils` | README.md,bin/utils/nomarchy-docs-scripts, +17 more | `kept` | |
| `nomarchy-theme-bg-install` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-bg-next` | `themes/engine/scripts` | README.md,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-theme-bg-set` | `themes/engine/scripts` | core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua,features/scripts/utils/nomarchy-wallpaper | `kept` | |
| `nomarchy-theme-bg-set` | `themes/engine/scripts` | features/apps/elephant/config/menus/nomarchy_background_selector.lua,features/scripts/utils/nomarchy-wallpaper | `kept` | |
| `nomarchy-theme-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,themes/engine/scripts/nomarchy-theme-next | `kept` | |
| `nomarchy-theme-install` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-theme, +2 more | `kept` | |
| `nomarchy-theme-next` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-refresh` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-remove` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-set` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/elephant/nomarchy_themes.lua, +9 more | `kept` | |
| `nomarchy-theme-set` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/apps/elephant/config/menus/nomarchy_themes.lua, +9 more | `kept` | |
| `nomarchy-theme-set-keyboard` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | |
| `nomarchy-theme-set-keyboard-asus-rog` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | |
| `nomarchy-theme-set-keyboard-f16` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | |
@@ -170,22 +166,17 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-tui-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-tui-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-tz-select` | `core/system/scripts` | features/desktop/waybar/config/config.jsonc,features/scripts/utils/nomarchy-menu, +2 more | `kept` | |
| `nomarchy-update` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/mako/core.ini, +4 more | `kept` | |
| `nomarchy-update-available` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-update` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/mako/core.ini, +5 more | `kept` | |
| `nomarchy-update-available` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-day/config.jsonc, +1 more | `kept` | |
| `nomarchy-update-firmware` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-update-time` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-upload-log` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug | `kept` | |
| `nomarchy-version` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug, +1 more | `kept` | |
| `nomarchy-voxtype-config` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-voxtype-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-voxtype-model` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-voxtype-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-voxtype-status` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-wallpaper` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/autostart.conf, +3 more | `kept` | |
| `nomarchy-webapp-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-webapp-remove-all | `kept` | |
| `nomarchy-webapp-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-webapp-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf,features/scripts/utils/nomarchy-installed-summary, +1 more | `kept` | |
| `nomarchy-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf,core/home/config/nomarchy/hooks/post-install.sample, +2 more | `kept` | |
| `nomarchy-wifi-powersave` | `core/system/scripts` | features/scripts/utils/nomarchy-sys-update,installer/install.sh | `kept` | |
| `nomarchy-windows-vm` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |

View File

@@ -20,7 +20,10 @@ Nomarchy is a NixOS-based distribution characterized by its **Modular Merging Ar
- **Downstream:** A user's installation (typically in `/etc/nixos/`) imports the Upstream flake. The user layers their own `system.nix` and `home.nix` on top, overriding or extending the Upstream settings using native NixOS `lib.mkDefault` and `lib.mkForce` patterns.
### Hybrid Declarative State
While the system is defined declaratively, Nomarchy uses a small, local state file (`~/.config/nomarchy/state.json`) to manage user preferences like the active theme, fonts, and feature toggles. This allows for instant UI feedback (via the `env-update` script) without requiring a full system rebuild for every cosmetic change.
Nomarchy balances the ease of a GUI with the power of declarative configuration.
- **Runtime Discovery:** Local state files (`~/.config/nomarchy/state.json` and `/etc/nixos/state.json`) handle personal choices like theme, font, and UI toggles. Changes made via the Theme Picker or Welcome Wizard update these files and provide instant visual feedback (via the `env-update` script).
- **Declarative Persistence:** To ensure these choices are permanent and reproducible, the `nomarchy-state-sync-nix` script automatically mirrors the home-side configuration into `/etc/nixos/nomarchy-state.nix`. This file is imported by the user's `home.nix`.
- **Solidification:** When you run `nomarchy-env-update`, your current runtime state is "solidified" into the Nix configuration, preventing your settings from reverting to defaults during the next full system rebuild.
---

210
docs/WORKING-ON-NOMARCHY.md Normal file
View File

@@ -0,0 +1,210 @@
# Working on Nomarchy — a practical orientation
This is the "I just want to change something without getting lost" guide. It is
deliberately short. For the full architecture, see [STRUCTURE.md](STRUCTURE.md);
for every option, [OPTIONS.md](OPTIONS.md).
---
## 1. The 60-second mental model
Nomarchy is a NixOS flake. Three layers, top to bottom:
```
core/ foundation → the OS + base user env that must always exist
features/ the desktop → apps, Hyprland/waybar, user scripts (opt-in modules)
themes/ the look → palettes (data) + engine (logic) + templates (.tpl)
```
Everything else is plumbing: `lib/` (shared Nix helpers), `installer/` + `hosts/`
(how it gets onto a disk), `bin/` (repo tooling, **not** shipped to users),
`docs/`.
The flake wires it up as two NixOS/HM modules:
- `nixosModules.system``core/system/` (the OS)
- `nixosModules.home``features/` (which itself imports `core/home/` + the theme engine)
### The two things that confuse everyone
There are **two** places that look like "app config," and they are *different
concepts*:
| Path | What it is |
|------|-----------|
| `features/apps/<app>/` | The **module** for an app — turns it on, sets its config. This is *code*. |
| `core/home/config/nomarchy/default/<app>/` | A **payload** of files copied verbatim to `~/.config/nomarchy/default/<app>/` at rebuild. This is *data* that scripts + the theme engine read at runtime. |
So when you saw `alacritty` in both trees: `features/apps/alacritty/` is the app
module; `core/home/config/nomarchy/default/alacritty/screensaver.toml` is just a
screensaver file that happens to be named after alacritty. Not a duplicate.
**Rule of thumb:** changing how an app *behaves*`features/apps/`. Changing a
file that lands in the user's home and gets read at runtime → `core/home/config/`.
---
## 2. "I want to change X" → go here
| I want to… | Go to |
|------------|-------|
| Tweak an app's settings (kitty, btop, tmux…) | `features/apps/<app>/default.nix` (+ its `config/`) |
| Change Hyprland behaviour / keybinds | `features/desktop/hyprland/` and `core/home/config/nomarchy/default/hypr/` |
| Change the status bar | `features/desktop/waybar/` |
| Add/remove a theme | `themes/palettes/<name>/` |
| Change how theming is applied | `themes/engine/loader.nix` |
| Add a **user** command (`nomarchy-foo`) | `features/scripts/utils/` |
| Add a **system/root** command | `core/system/scripts/` |
| Add a NixOS option (`nomarchy.system.*`) | `core/system/options.nix` |
| Add a HM option (`nomarchy.*`) | `core/home/options.nix` |
| Change what the installer writes | `installer/install.sh` |
When in doubt, grep for an existing example of the thing you're changing and copy
its shape — the repo is very consistent *within* each of these buckets.
---
## 3. The build/test loop
You almost never need a full install to test a change. From the repo root:
```bash
# Evaluate + build the whole system WITHOUT activating it (safe, no sudo):
nixos-rebuild build --flake .#default --impure
# → prints "Done. The new configuration is /nix/store/…" if it builds.
# (drops a ./result symlink you can delete)
```
On a real install, the user-facing commands are:
```bash
sys-update # sudo nixos-rebuild switch --flake .#default --impure (system)
env-update # home-manager switch --flake .#default --impure (user env)
```
`bin/utils/` holds repo tooling that regenerates the auto-docs
(`SCRIPTS.md`, `KEYBINDINGS.md`). A pre-commit hook runs them; you rarely call
them by hand.
---
## 4. Where scripts live (the one rule)
There are **four** script homes, by **execution context**, not by topic:
| Dir | Context | Example |
|-----|---------|---------|
| `bin/utils/` | Repo tooling, never shipped | `nomarchy-docs-scripts` |
| `core/system/scripts/` | System / root / hardware | `nomarchy-setup-dns`, `nomarchy-toggle-hybrid-gpu` |
| `features/scripts/utils/` | User / desktop | `nomarchy-menu`, `nomarchy-launch-walker` |
| `themes/engine/scripts/` | Theme engine | **`nomarchy-theme-set`**, `nomarchy-theme-bg-set`, `nomarchy-theme-next` |
> **Heads-up:** the `nomarchy-theme-*` family lives in `themes/engine/scripts/`, **not**
> `features/scripts/utils/`. All four dirs are built onto the user's `$PATH`, so at
> runtime `nomarchy-theme-set "Tokyo Night"` just works regardless of which dir it's in —
> the split only matters when you're hunting for the *source*. Find any script's source
> fast with `grep -rl nomarchy-theme-set .` rather than guessing the directory.
New script? Ask: *does it need root or system packages?*`core/system/scripts/`.
*Is it user-facing desktop glue?*`features/scripts/utils/`. *Only useful inside
this repo?* → `bin/utils/`. Scripts are found by name on `$PATH`, so moving one
between the first two means changing the Nix derivation it's built into, not the
call sites.
---
## 5. How theming works (the short version)
1. The active theme name lives in **state**: `~/.config/nomarchy/state.json`
(runtime) → mirrored into `/etc/nixos/nomarchy-state.nix` for reproducibility.
2. `themes/engine/loader.nix` reads that name and deploys the matching themed
files (btop theme, kitty colors, waybar css…) into `~/.config/`.
3. `themes/templates/*.tpl` are filled with the palette's colors to produce
dynamic configs.
4. Switching a theme runs scripts that rewrite those files and then *reload* the
affected apps (this is what the `nomarchy-restart-*` family is for — each app
reloads differently: SIGUSR2, a full restart, etc.).
---
## 6. Walker + elephant (so you can decide on the menus later)
This is the bit you wanted to understand before changing anything. There are
**two separate programs**:
- **Walker** — the *front-end*. A Rust/GTK4 window that shows a list and a
search box. It's what you see. Started as a user service
(`programs.walker`, `runAsService = true`).
- **elephant** — the *back-end*. A Go daemon that actually produces the data
(apps, calculator, clipboard, emoji, and **custom menus**). Walker talks to it
over a Unix socket. Config lives in `features/apps/elephant/config/`, deployed
to `~/.config/elephant/`.
Think: **Walker draws, elephant supplies.**
### How a menu reaches the screen
There are two completely different paths, and only one of them touches Lua:
**Path A — `--dmenu` (no elephant data providers, no Lua).**
Used by `nomarchy-menu`, `nomarchy-font`, the keybindings viewer, etc.
You pipe plain text lines into `walker --dmenu`; Walker shows them and prints the
chosen line back. **Text only** — Walker's dmenu mode literally cannot show icons
or an image preview pane (verified in its source: each line becomes `item.text`
and the preview box is hidden). This is the simple, dependency-free path.
```
echo -e "Option A\nOption B" | walker --dmenu -p "Pick…"
```
**Path B — elephant custom menu providers (`-m menus:<name>`).**
Used by the theme picker and wallpaper picker. Here elephant loads a *menu
provider* from `~/.config/elephant/menus/`. A menu provider can be:
- a **TOML** file with a *static* list of entries — each entry can have an
`icon`, a `preview`/`preview_type`, and `actions`; or
- a **Lua** file (`run = "lua:…"`) whose `GetEntries()` returns a *dynamic*
list built at runtime.
The theme/wallpaper menus need a *variable* list (one entry per theme/wallpaper,
discovered on disk) **and** an image preview. In elephant, the only built-in way
to generate a variable-length list is Lua's `GetEntries()`. That's the entire
reason `nomarchy_themes.lua` and `nomarchy_background_selector.lua` exist — and
they're the *only* Lua in the whole repo.
### The decision space (for later)
If you want to drop Lua but keep Walker, the trade is purely about previews:
- **Keep previews** → replace the two `.lua` files with a small **bash generator**
that writes *static* elephant TOML menus (one `[[entries]]` per theme/wallpaper,
each with `preview = "…/preview.png"`), regenerated at rebuild + on theme
switch. No Lua, keeps the preview pane, adds one generator script.
- **Drop previews** → route the theme/wallpaper pickers through `walker --dmenu`
like the other menus. Deletes both `.lua` files, the elephant menu config for
them, and the `pkgs.lua` dependency. Simplest possible; theme/wallpaper become
plain text lists.
Nothing here is decided yet — this section is just the map.
### Key files for the menu system
| File | Role |
|------|------|
| `features/scripts/utils/nomarchy-launch-walker` | Wrapper: starts elephant + walker services, routes `--dmenu` vs provider calls |
| `features/scripts/utils/nomarchy-menu` | The big interactive menu (all Path A / dmenu) |
| `features/apps/walker.nix` | Walker module + config (prefixes, providers, theme) |
| `features/apps/elephant/config/` | elephant providers (calc, symbols, **menus/**) |
| `features/apps/elephant/config/menus/*.lua` | The two dynamic preview menus (the only Lua) |
---
## 7. Gotchas worth knowing
- **`--impure` is required** on rebuilds — the config reads runtime state
(`state.json`) outside the flake.
- **`docs/SCRIPTS.md` and `docs/KEYBINDINGS.md` are auto-generated.** Don't edit by
hand; the pre-commit hook (and CI) regenerate and verify them.
- **The deep `core/home/config/nomarchy/default/…` tree is a payload**, deployed
wholesale to `~/.config/nomarchy/default/`. Moving files out of it will break the
scripts/loader that read those exact runtime paths.
- **Two parallel module systems**: `core/system` = NixOS (root), `features` +
`core/home` = Home Manager (user). A setting only works if it's in the right one.

View File

@@ -1,25 +1,27 @@
{ config, pkgs, lib, ... }:
{
programs.alacritty = {
enable = lib.mkDefault true;
settings = lib.mkDefault {
env = {
TERM = "xterm-256color";
};
terminal = {
osc52 = "CopyPaste";
};
window = {
padding = { x = 14; y = 14; };
decorations = "None";
};
keyboard = {
bindings = [
{ key = "Insert"; mods = "Shift"; action = "Paste"; }
{ key = "Insert"; mods = "Control"; action = "Copy"; }
{ key = "Return"; mods = "Shift"; chars = "\\u001B\\r"; }
];
config = lib.mkIf config.nomarchy.apps.alacritty.enable {
programs.alacritty = {
enable = lib.mkDefault true;
settings = lib.mkDefault {
env = {
TERM = "xterm-256color";
};
terminal = {
osc52 = "CopyPaste";
};
window = {
padding = { x = 14; y = 14; };
decorations = "None";
};
keyboard = {
bindings = [
{ key = "Insert"; mods = "Shift"; action = "Paste"; }
{ key = "Insert"; mods = "Control"; action = "Copy"; }
{ key = "Return"; mods = "Shift"; chars = "\\u001B\\r"; }
];
};
};
};
};

View File

@@ -1,5 +1,8 @@
{ config, pkgs, lib, ... }:
{
xdg.configFile."btop/btop.conf".source = ./config/btop.conf;
config = lib.mkIf config.nomarchy.apps.btop.enable {
programs.btop.enable = lib.mkDefault true;
xdg.configFile."btop/btop.conf".source = ./config/btop.conf;
};
}

View File

@@ -46,7 +46,9 @@ function GetEntries()
for _, wallpaper_dir in ipairs(dirs) do
local handle = io.popen(
"find " .. ShellEscape(wallpaper_dir)
-- -L so -type f follows the home-manager symlinks the backgrounds are
-- deployed as (the nomarchy_themes.lua provider does the same).
"find -L " .. ShellEscape(wallpaper_dir)
.. " -maxdepth 1 -type f \\( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' -o -name '*.gif' -o -name '*.bmp' -o -name '*.webp' \\) 2>/dev/null | sort"
)
if handle then

View File

@@ -1,8 +1,10 @@
{ config, pkgs, lib, ... }:
{
xdg.configFile."elephant" = {
source = ./config;
recursive = true;
config = lib.mkIf config.nomarchy.apps.elephant.enable {
xdg.configFile."elephant" = {
source = ./config;
recursive = true;
};
};
}

View File

@@ -1,5 +1,7 @@
{ config, pkgs, lib, ... }:
{
xdg.configFile."ghostty/config".source = ./config/config;
config = lib.mkIf config.nomarchy.apps.ghostty.enable {
xdg.configFile."ghostty/config".source = ./config/config;
};
}

View File

@@ -1,5 +1,8 @@
{ config, pkgs, lib, ... }:
{
xdg.configFile."kitty/kitty.conf".source = ./config/kitty.conf;
config = lib.mkIf config.nomarchy.apps.kitty.enable {
programs.kitty.enable = lib.mkDefault true;
xdg.configFile."kitty/kitty.conf".source = ./config/kitty.conf;
};
}

View File

@@ -1,5 +1,8 @@
{ config, pkgs, lib, ... }:
{
xdg.configFile."lazygit/config.yml".source = ./config/config.yml;
config = lib.mkIf config.nomarchy.apps.lazygit.enable {
home.packages = [ pkgs.lazygit ];
xdg.configFile."lazygit/config.yml".source = ./config/config.yml;
};
}

View File

@@ -1,41 +1,43 @@
{ config, pkgs, lib, ... }:
{
services.swayosd = {
enable = lib.mkDefault true;
stylePath = lib.mkDefault "${config.home.homeDirectory}/.config/swayosd/style.css";
config = lib.mkIf config.nomarchy.apps.swayosd.enable {
services.swayosd = {
enable = lib.mkDefault true;
stylePath = lib.mkDefault "${config.home.homeDirectory}/.config/swayosd/style.css";
};
xdg.configFile."swayosd/style.css".text = lib.mkDefault ''
@define-color background-color #${config.colorScheme.palette.base00};
@define-color border-color #${config.colorScheme.palette.base0E};
@define-color label #${config.colorScheme.palette.base05};
@define-color image #${config.colorScheme.palette.base05};
@define-color progress #${config.colorScheme.palette.base0B};
window {
border-radius: 0;
opacity: 0.97;
border: 2px solid @border-color;
background-color: @background-color;
}
label {
font-family: '${config.nomarchy.fonts.monospace}';
font-size: 11pt;
color: @label;
}
image {
color: @image;
}
progressbar {
border-radius: 0;
}
progress {
background-color: @progress;
}
'';
};
xdg.configFile."swayosd/style.css".text = lib.mkDefault ''
@define-color background-color #${config.colorScheme.palette.base00};
@define-color border-color #${config.colorScheme.palette.base0E};
@define-color label #${config.colorScheme.palette.base05};
@define-color image #${config.colorScheme.palette.base05};
@define-color progress #${config.colorScheme.palette.base0B};
window {
border-radius: 0;
opacity: 0.97;
border: 2px solid @border-color;
background-color: @background-color;
}
label {
font-family: '${config.nomarchy.fonts.monospace}';
font-size: 11pt;
color: @label;
}
image {
color: @image;
}
progressbar {
border-radius: 0;
}
progress {
background-color: @progress;
}
'';
}

View File

@@ -1,5 +1,8 @@
{ config, pkgs, lib, ... }:
{
xdg.configFile."tmux/tmux.conf".source = ./config/tmux.conf;
config = lib.mkIf config.nomarchy.apps.tmux.enable {
programs.tmux.enable = lib.mkDefault true;
xdg.configFile."tmux/tmux.conf".source = ./config/tmux.conf;
};
}

View File

@@ -96,15 +96,7 @@ let
];
in
{
options.nomarchy.vscode = {
devExtensions = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to install development extensions for VSCode.";
};
};
config = {
config = lib.mkIf config.nomarchy.apps.vscode.enable {
programs.vscode = {
enable = lib.mkDefault true;
package = lib.mkDefault pkgs.vscode;
@@ -118,7 +110,7 @@ in
};
extensions = lib.mkDefault (
themeExtensions
++ lib.optionals config.nomarchy.vscode.devExtensions devExtensions
++ lib.optionals config.nomarchy.apps.vscode.devExtensions devExtensions
);
};
};

View File

@@ -1,49 +1,51 @@
{ config, pkgs, lib, ... }:
{
home.packages = [ pkgs.lua ];
config = lib.mkIf config.nomarchy.apps.walker.enable {
home.packages = [ pkgs.lua ];
programs.walker = {
enable = lib.mkDefault true;
runAsService = lib.mkDefault true;
config = lib.mkDefault {
theme = "nomarchy";
ui = {
anchors = {
top = true;
programs.walker = {
enable = lib.mkDefault true;
runAsService = lib.mkDefault true;
config = lib.mkDefault {
theme = "nomarchy";
ui = {
anchors = {
top = true;
};
};
selection_wrap = true;
hide_action_hints = true;
placeholders = {
"default" = { input = " Search..."; list = "No Results"; };
};
keybinds = {
quick_activate = [];
};
columns = {
symbols = 1;
};
providers = {
max_results = 256;
default = [
"desktopapplications"
"websearch"
];
prefixes = [
{ prefix = "/"; provider = "providerlist"; }
{ prefix = "."; provider = "files"; }
{ prefix = ":"; provider = "symbols"; }
{ prefix = "="; provider = "calc"; }
{ prefix = "@"; provider = "websearch"; }
{ prefix = "$"; provider = "clipboard"; }
];
};
};
selection_wrap = true;
hide_action_hints = true;
placeholders = {
"default" = { input = " Search..."; list = "No Results"; };
themes."nomarchy" = lib.mkDefault {
style = ''
@import "${config.home.homeDirectory}/.config/nomarchy/current/theme/apps/walker/style.css";
'';
};
keybinds = {
quick_activate = [];
};
columns = {
symbols = 1;
};
providers = {
max_results = 256;
default = [
"desktopapplications"
"websearch"
];
prefixes = [
{ prefix = "/"; provider = "providerlist"; }
{ prefix = "."; provider = "files"; }
{ prefix = ":"; provider = "symbols"; }
{ prefix = "="; provider = "calc"; }
{ prefix = "@"; provider = "websearch"; }
{ prefix = "$"; provider = "clipboard"; }
];
};
};
themes."nomarchy" = lib.mkDefault {
style = ''
@import "${config.home.homeDirectory}/.config/nomarchy/current/theme/apps/walker/style.css";
'';
};
};
}

View File

@@ -38,15 +38,12 @@ in
# Enable neovim program module (required for stylix integration)
programs.neovim.enable = lib.mkDefault true;
home.packages = lib.mkDefault (with pkgs; [
# Core applications
firefox
xfce.thunar
# Media
imv # Image viewer
mpv # Video player
# NOT mkDefault: home.packages is a list that MERGES across modules. At
# mkDefault priority this whole curated list is dropped the moment any
# other module sets home.packages at normal priority (features/scripts,
# the installer's profile packages), silently removing firefox, mako,
# hyprlock, mpv, etc. — which broke notifications and the lock screen.
home.packages = (with pkgs; [
# Hyprland ecosystem
swww # Wallpaper daemon
mako # Notification daemon
@@ -69,7 +66,8 @@ in
gum # TUI components for scripts
xdg-terminal-exec
swaybg
rofi-wayland
rofi # rofi-wayland was merged into rofi upstream
calcurse # TUI calendar
# Theming — cursor package stays here; icon theme packages are pulled in
# dynamically by themes/engine/stylix.nix via nomarchyLib.iconThemePackage
@@ -77,3 +75,4 @@ in
bibata-cursors
]);
}

View File

@@ -1,23 +0,0 @@
# See https://wiki.hyprland.org/Configuring/Monitors/
# List current monitors and resolutions possible: hyprctl monitors
# Format: monitor = [port], resolution, position, scale
# Optimized for retina-class 2x displays, like 13" 2.8K, 27" 5K, 32" 6K.
#env = GDK_SCALE,2
#monitor=,preferred,auto,auto
# Good compromise for 27" or 32" 4K monitors (but fractional!)
# env = GDK_SCALE,1.75
# monitor=,preferred,auto,1.6
# Straight 1x setup for low-resolution displays like 1080p or 1440p
# Or for ultrawide monitors like 34" 3440x1440 or 49" 5120x1440
env = GDK_SCALE,1
monitor=,highres,auto,1
# Portrait/rotated secondary monitor (transform: 1 = 90°, 3 = 270°)
# monitor = DP-2, preferred, auto, 1, transform, 1
# Example for Framework 13 w/ 6K XDR Apple display
# monitor = DP-5, 6016x3384@60, auto, 2
# monitor = eDP-1, 2880x1920@120, auto, 2

View File

@@ -53,7 +53,11 @@ in
# live under ~/.config/nomarchy/default/hypr/ and are deployed by the
# core/home bulk-nomarchy dir, so duplicating them here was dead surface.
xdg.configFile."hypr/nomarchy.conf".source = ./config/nomarchy.conf;
xdg.configFile."hypr/monitors.conf".source = lib.mkDefault ./config/monitors.conf;
xdg.configFile."hypr/monitors.conf".text = lib.mkDefault ''
# Auto-generated by Nomarchy features/desktop/hyprland/default.nix
# monitor = [port], resolution, position, scale
monitor = , highres, auto, ${config.nomarchy.hyprland.scale}
'';
xdg.configFile."hypr/input.conf".source = lib.mkDefault ./config/input.conf;
xdg.configFile."hypr/bindings.conf".source = lib.mkDefault ./config/bindings.conf;

View File

@@ -5,7 +5,7 @@
"spacing": 0,
"height": 26,
"modules-left": ["custom/nomarchy", "hyprland/workspaces"],
"modules-center": ["clock", "custom/update", "custom/voxtype", "custom/screenrecording-indicator", "custom/idle-indicator", "custom/notification-silencing-indicator"],
"modules-center": ["clock", "custom/update", "custom/screenrecording-indicator", "custom/idle-indicator", "custom/notification-silencing-indicator"],
"modules-right": [
"tray",
"bluetooth",
@@ -43,7 +43,7 @@
"format": "",
"on-click": "nomarchy-menu",
"on-click-right": "xdg-terminal-exec",
"tooltip-format": "Nomarchy Menu\n\nSuper + Alt + Space"
"tooltip-format": "Nomarchy Menu\n\nSuper + Shift + Space"
},
"custom/update": {
"format": "",
@@ -58,12 +58,15 @@
"interval": 5,
"format": "󰍛",
"on-click": "nomarchy-launch-or-focus-tui btop",
"on-click-right": "alacritty"
"on-click-right": "alacritty",
"tooltip-format": "Activity\n\nSuper + Ctrl + T"
},
"clock": {
"format": "{:L%A %H:%M}",
"format-alt": "{:L%d %B W%V %Y}",
"tooltip": false,
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T",
"on-click": "nomarchy-launch-or-focus-tui calcurse",
"on-click-right": "nomarchy-launch-floating-terminal-with-presentation nomarchy-tz-select"
},
"network": {
@@ -72,9 +75,9 @@
"format-wifi": "{icon}",
"format-ethernet": "󰀂",
"format-disconnected": "󰤮",
"tooltip-format-wifi": "{essid} ({frequency} GHz)\n⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}",
"tooltip-format-ethernet": "⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}",
"tooltip-format-disconnected": "Disconnected",
"tooltip-format-wifi": "{essid} ({frequency} GHz)\n⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}\n\nSuper + Ctrl + W",
"tooltip-format-ethernet": "⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}\n\nSuper + Ctrl + W",
"tooltip-format-disconnected": "Disconnected\n\nSuper + Ctrl + W",
"interval": 3,
"spacing": 1,
"on-click": "nomarchy-launch-wifi"
@@ -89,8 +92,9 @@
"default": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"]
},
"format-full": "󰂅",
"tooltip-format-discharging": "{power:>1.0f}W↓ {capacity}%",
"tooltip-format-charging": "{power:>1.0f}W {capacity}%",
"tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B",
"tooltip-format-discharging": "{power:>1.0f}W {capacity}%\n\nSuper + Ctrl + Alt + B",
"tooltip-format-charging": "{power:>1.0f}W↑ {capacity}%\n\nSuper + Ctrl + Alt + B",
"interval": 5,
"on-click": "nomarchy-menu power",
"states": {
@@ -104,14 +108,14 @@
"format-disabled": "󰂲",
"format-connected": "󰂱",
"format-no-controller": "",
"tooltip-format": "Devices connected: {num_connections}",
"tooltip-format": "Devices connected: {num_connections}\n\nSuper + Ctrl + B",
"on-click": "nomarchy-launch-bluetooth"
},
"pulseaudio": {
"format": "{icon}",
"on-click": "nomarchy-launch-audio",
"on-click-right": "pamixer -t",
"tooltip-format": "Playing at {volume}%",
"tooltip-format": "Playing at {volume}%\n\nSuper + Ctrl + A",
"scroll-step": 5,
"format-muted": "",
"format-icons": {
@@ -138,19 +142,6 @@
"signal": 10,
"return-type": "json"
},
"custom/voxtype": {
"exec": "nomarchy-voxtype-status",
"return-type": "json",
"format": "{icon}",
"format-icons": {
"idle": "",
"recording": "󰍬",
"transcribing": "󰔟"
},
"tooltip": true,
"on-click-right": "nomarchy-voxtype-config",
"on-click": "nomarchy-voxtype-model"
},
"tray": {
"icon-size": 12,
"spacing": 17

View File

@@ -97,12 +97,3 @@ tooltip {
#custom-notification-silencing-indicator.active {
color: #a55555;
}
#custom-voxtype {
min-width: 12px;
margin: 0 0 0 7.5px;
}
#custom-voxtype.recording {
color: #a55555;
}

View File

@@ -47,7 +47,10 @@ in
style = lib.mkDefault (builtins.readFile styleFile);
};
home.packages = lib.mkDefault (with pkgs; [
# Not mkDefault: home.packages is a list other modules set at normal
# priority, so a mkDefault def is dropped — leaving font-awesome (waybar's
# icon font) uninstalled. Merge it in.
home.packages = with pkgs; [
font-awesome
]);
];
}

View File

@@ -6,11 +6,10 @@
"height": 60,
"layer": "top",
"position": "top",
"output": "DP-2",
"spacing": 15,
"modules-left": ["custom/launcher", "clock", "clock#date"],
"modules-center": ["wlr/workspaces"],
"modules-right": ["pulseaudio", "network", "battery", "custom/powermenu"],
"modules-right": ["custom/update", "pulseaudio", "network", "battery", "custom/powermenu"],
"wlr/workspaces": {
"disable-scroll": true,
@@ -35,12 +34,20 @@
"custom/launcher": {
"interval": "once",
"format": "󰣇",
"on-click": "pkill wofi || wofi --show drun --term=kitty --width=20% --height=50% --columns 1 -I -s ~/.config/wofi/themes/everforest-light.css -o $MAIN_DISPLAY",
"tooltip": false
"on-click": "nomarchy-menu",
"tooltip-format": "Nomarchy Menu\n\nSuper + Shift + Space"
},
"custom/update": {
"format": "",
"exec": "nomarchy-update-available",
"on-click": "nomarchy-launch-floating-terminal-with-presentation nomarchy-update",
"tooltip-format": "Nomarchy update available",
"signal": 7,
"interval": 21600
},
"backlight": {
"device": "nvidia_0",
"max-length": "4",
"format": "{icon}",
"tooltip-format": "{percent}%",
@@ -78,15 +85,17 @@
"car": "",
"default": ["", "", ""]
},
"on-click-right": "pavucontrol",
"on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle"
"on-click": "nomarchy-launch-audio",
"on-click-right": "pamixer -t",
"tooltip-format": "Playing at {volume}%\n\nSuper + Ctrl + A"
},
"network": {
"format-wifi": " {signalStrength}%",
"format-ethernet": " {signalStrength}%",
"format-disconnected": "󰤭",
"on-click": "sh ~/.config/wofi/scripts/wifimenu.sh"
"on-click": "nomarchy-launch-wifi",
"tooltip-format": "Wifi controls\n\nSuper + Ctrl + W"
},
"battery": {
@@ -105,20 +114,26 @@
"format-plugged": " {capacity}%",
"format-alt": "{icon} {capacity}%",
"format-full": " 100%",
"format-icons": ["", "", "", "", "", "", "", "", "", ""]
"format-icons": ["", "", "", "", "", "", "", "", "", ""],
"tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B"
},
"clock": {
"format": " {:%H:%M}"
"format": " {:%H:%M}",
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T"
},
"clock#date": {
"format": " {:%A, %B %d, %Y}"
"format": " {:%A, %B %d, %Y}",
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T",
"on-click": "nomarchy-launch-or-focus-tui calcurse"
},
"custom/powermenu": {
"format": "",
"on-click": "pkill wofi || sh .config/wofi/scripts/powermenu.sh 'everforest-light' '--height=17% -o $MAIN_DISPLAY'",
"on-click": "nomarchy-menu power",
"tooltip": false
}
}

View File

@@ -9,7 +9,7 @@
"spacing": 15,
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
"modules-center": ["hyprland/workspaces"],
"modules-right": [ "idle_inhibitor", "pulseaudio", "tray", "custom/powermenu"],
"modules-right": ["custom/update", "idle_inhibitor", "pulseaudio", "custom/battery", "tray", "custom/powermenu"],
"hyprland/workspaces": {
"disable-scroll": true,
@@ -40,7 +40,7 @@
"interval": "once",
"format": "\uf000",
"on-click": "nomarchy-menu",
"tooltip-format": "Application Launcher"
"tooltip-format": "Nomarchy Menu\n\nSuper + Shift + Space"
},
"custom/update": {
@@ -73,20 +73,6 @@
"return-type": "json"
},
"custom/voxtype": {
"exec": "nomarchy-voxtype-status",
"return-type": "json",
"format": "{icon}",
"format-icons": {
"idle": "",
"recording": "󰍬",
"transcribing": "󰔟"
},
"tooltip": true,
"on-click-right": "nomarchy-voxtype-config",
"on-click": "nomarchy-voxtype-model"
},
"backlight": {
"max-length": "4",
"format": "{icon} {percent}%",
@@ -130,37 +116,41 @@
"on-click": "pamixer -t",
"on-click-middle": "nomarchy-launch-audio",
"on-click-right": "pavucontrol",
"tooltip-format": "{icon} {desc} | {volume}%"
"tooltip-format": "{icon} {desc} | {volume}%\n\nSuper + Ctrl + A"
},
"network": {
"format-wifi": " {signalStrength}%",
"format-ethernet": " {signalStrength}%",
"format-disconnected": "󰤭",
"on-click": "nomarchy-launch-wifi"
"on-click": "nomarchy-launch-wifi",
"tooltip-format": "Wifi controls\n\nSuper + Ctrl + W"
},
"custom/battery": {
"interval": 30,
"format": "{}",
"exec": "nomarchy-battery-status",
"on-click": "nomarchy-menu power"
"on-click": "nomarchy-menu power",
"tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B"
},
"clock": {
"format": " {:%H:%M}",
"tooltip": false
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T"
},
"clock#date": {
"format": " {:%A, %B %d, %Y}",
"on-click": "kitty calcurse",
"tooltip-format": "Open calendar"
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T",
"on-click": "nomarchy-launch-or-focus-tui calcurse"
},
"custom/powermenu": {
"format": "",
"on-click": "wlogout",
"on-click": "nomarchy-menu power",
"tooltip": false
}
}

View File

@@ -1,110 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# nomarchy-docs-keybindings
#
# Regenerates docs/KEYBINDINGS.md from the Hyprland binding files. Run from the
# repo root or anywhere — paths are resolved relative to this script.
#
# nomarchy-docs-keybindings # write to stdout
# nomarchy-docs-keybindings --out docs/KEYBINDINGS.md
#
# Source files in render order. Each entry is "<repo-relative path>|<title>".
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
sources=(
"core/home/config/nomarchy/default/hypr/bindings/utilities.conf|Utilities"
"core/home/config/nomarchy/default/hypr/bindings/tiling.conf|Tiling"
"core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf|Tiling (v2)"
"core/home/config/nomarchy/default/hypr/bindings/clipboard.conf|Clipboard"
"core/home/config/nomarchy/default/hypr/bindings/media.conf|Media keys"
"features/desktop/hyprland/config/bindings.conf|Apps & web shortcuts"
)
prettify_key() {
case "$1" in
code:10) echo "1" ;; code:11) echo "2" ;; code:12) echo "3" ;;
code:13) echo "4" ;; code:14) echo "5" ;; code:15) echo "6" ;;
code:16) echo "7" ;; code:17) echo "8" ;; code:18) echo "9" ;;
code:19) echo "0" ;;
XF86AudioRaiseVolume) echo "Volume Up" ;;
XF86AudioLowerVolume) echo "Volume Down" ;;
XF86AudioMute) echo "Mute" ;;
XF86AudioMicMute) echo "Mic Mute" ;;
XF86AudioPlay) echo "Play/Pause" ;;
XF86AudioStop) echo "Stop" ;;
XF86AudioNext) echo "Next Track" ;;
XF86AudioPrev) echo "Previous Track" ;;
XF86MonBrightnessUp) echo "Brightness Up" ;;
XF86MonBrightnessDown) echo "Brightness Down" ;;
XF86KbdBrightnessUp) echo "Kbd Brightness Up" ;;
XF86KbdBrightnessDown) echo "Kbd Brightness Down" ;;
XF86KbdLightOnOff) echo "Kbd Backlight" ;;
*) echo "$1" ;;
esac
}
trim() { sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//'; }
render_section() {
local file="$1" title="$2"
[[ ! -f "$repo_root/$file" ]] && return
local rows
rows=$(grep -E '^[[:space:]]*bind[a-z]*[[:space:]]*=' "$repo_root/$file" || true)
[[ -z "$rows" ]] && return
local body=""
while IFS= read -r line; do
# Strip the "bindXXX =" prefix.
local rhs="${line#*=}"
local mods key desc
IFS=',' read -r mods key desc _ <<<"$rhs"
mods=$(printf '%s' "${mods:-}" | trim)
key=$(printf '%s' "${key:-}" | trim)
desc=$(printf '%s' "${desc:-}" | trim)
[[ -z "$desc" ]] && continue # skip non-descriptive bindings
[[ -z "$mods" ]] && mods="—"
key=$(prettify_key "$key")
body+=$(printf '| %s | %s | %s |\n' "$mods" "$key" "$desc")
body+=$'\n'
done <<<"$rows"
[[ -z "$body" ]] && return
printf '\n## %s\n\n' "$title"
printf '_Source: `%s`_\n\n' "$file"
printf '| Modifiers | Key | Action |\n'
printf '| --- | --- | --- |\n'
printf '%s' "$body"
}
main() {
cat <<'HEADER'
# Nomarchy Keybindings
Auto-generated from the Hyprland binding files. **Do not edit by hand.**
Re-run the generator after changing any `bindings/*.conf`:
```bash
./bin/utils/nomarchy-docs-keybindings --out docs/KEYBINDINGS.md
```
`SUPER` is the Meta / Win key. `code:NN` keys (X11 digit keycodes) are
shown as the digit they correspond to. Media keys (`XF86Audio*`,
`XF86MonBrightness*`, …) are prettified.
HEADER
for entry in "${sources[@]}"; do
render_section "${entry%|*}" "${entry#*|}"
done
}
out=""
if [[ "${1:-}" == "--out" ]]; then
out="${2:?--out needs a path}"; shift 2
fi
if [[ -n "$out" ]]; then
main >"$out"
else
main
fi

View File

@@ -1,275 +0,0 @@
#!/usr/bin/env bash
set -e
# Generator tolerates "no matches" exit codes from grep | sort.
# pipefail and -e off; -u stays.
set -u
# nomarchy-docs-scripts
#
# Regenerates docs/SCRIPTS.md from the repo state. Produces:
# 1. Header + status legend + regen instructions.
# 2. Table of every nomarchy-* script (location, callers, status).
# 3. Table of every menu entry in features/scripts/utils/nomarchy-menu
# (submenu, label, target command, status).
# 4. Snapshot list of orphaned references (called somewhere, no script).
#
# Status heuristic in Phase A:
# kept — file exists AND is called from at least one *.nix / *.conf /
# shell file outside its own directory.
# unused? — file exists but no caller found. Phase B decides delete-dead
# vs intentional public API.
# missing — referenced but no file. Phase B decides port-from-omarchy
# vs delete-dead vs stub-with-notify.
#
# nomarchy-docs-scripts # write to stdout
# nomarchy-docs-scripts --out docs/SCRIPTS.md
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$repo_root"
# --- Inventory -------------------------------------------------------------
# Where scripts live, in render order.
declare -A loc_label=(
["features/scripts/utils"]="features/scripts/utils"
["core/system/scripts"]="core/system/scripts"
["themes/engine/scripts"]="themes/engine/scripts"
)
script_dirs=(features/scripts/utils core/system/scripts themes/engine/scripts)
# Build name → location map (associative array of basename → repo-relative dir).
declare -A script_loc
for dir in "${script_dirs[@]}"; do
[[ -d "$dir" ]] || continue
while IFS= read -r f; do
script_loc["$(basename "$f")"]="$dir"
done < <(find "$dir" -maxdepth 1 -type f -name 'nomarchy-*')
done
# Find every nomarchy-* token referenced anywhere outside the script dirs.
# (We exclude the script files themselves so they don't list themselves as
# their own caller.)
# File types we search for references. *.md catches docs and README;
# branding/hook/extension files have varied or no extensions.
# *.lua catches elephant providers; *.ini catches mako on-button-* hooks;
# *.desktop catches MimeType-registered URL handlers.
grep_includes=(
--include='*.nix' --include='*.conf' --include='*.sh' --include='*.md'
--include='nomarchy-*' --include='*.jsonc' --include='*.json'
--include='*.toml' --include='*.ini' --include='*.lua'
--include='*.desktop' --include='*.txt' --include='*.sample'
)
search_dirs=(core features themes installer hosts bin lib README.md)
# Files whose mentions of nomarchy-* are documentation about the scripts,
# not real callers. Excluded from caller discovery so they don't promote
# every script to `kept`.
self_refs=(docs/SCRIPTS.md docs/ROADMAP.md docs/AGENT.md)
ref_files_per_cmd() {
local cmd="$1"
local self_pattern
self_pattern=$(IFS='|'; echo "${self_refs[*]}")
grep -rlE "\\b${cmd}\\b" \
"${grep_includes[@]}" \
"${search_dirs[@]}" 2>/dev/null \
| grep -vE "^(features/scripts/utils|core/system/scripts|themes/engine/scripts)/${cmd}$" \
| grep -vE "^(${self_pattern})$" \
| sort -u
}
# All distinct nomarchy-* tokens we see anywhere in the repo.
# Final char must be alphanumeric — dropping trailing-dash matches like
# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`).
# Restrict to grep_includes so binaries / tmpfiles don't pollute the set.
# The middle `grep -vE` drops lines where `nomarchy-*` is a derivation /
# tmp file / sudoers basename / systemd unit / flake output / docker
# container identifier — not a shell invocation — so they don't show up
# as fake "missing" references.
all_refs=$(grep -rhE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
"${grep_includes[@]}" \
"${search_dirs[@]}" 2>/dev/null \
| grep -vE \
-e '(pname|name)[[:space:]]*=[[:space:]]*"nomarchy-' \
-e '/tmp/nomarchy-' \
-e '/etc/sudoers\.d/[^"[:space:]]*nomarchy-' \
-e 'nixosConfigurations\.nomarchy-' \
-e 'packages\.[^.]+\.nomarchy-' \
-e '\./result/bin/run-nomarchy-' \
-e 'mktemp[[:space:]]+[^|]*-t[[:space:]]+nomarchy-' \
-e '(TIMER_NAME|NOPASSWD_FILE|UNIT_NAME)=.*nomarchy-' \
-e 'docker[[:space:]]+[^|]*nomarchy-' \
| grep -oE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
| grep -vE '^(nomarchy-plymouth|nomarchy-sddm-theme|nomarchy-live|nomarchy-rev|nomarchy-windows)$' \
| sort -u)
# The token-level denylist above covers identifiers whose ambiguity survives
# the line filter: `nomarchy-plymouth` / `nomarchy-sddm-theme` are Nix
# derivation names referenced as bare idents in `[...]` lists,
# `nomarchy-live` is an ISO label that shows up in comments, `nomarchy-rev`
# is `/etc/nomarchy-rev` (a file written by the ISO), and
# `nomarchy-windows` is a docker container name in compose heredocs.
# --- Render: header --------------------------------------------------------
main() {
cat <<'HEADER'
# Nomarchy Script & Menu Audit
Auto-generated table for [Pillar 3 of the roadmap](ROADMAP.md#3-pillar-script--menu-audit).
**Do not edit by hand.** Regenerate after script or menu changes:
```bash
./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md
```
The status column uses a Phase A heuristic — `kept` / `unused?` / `missing`.
Phase B (per-batch PRs) refines those into `port-from-omarchy`,
`delete-dead`, or `stub-with-notify` and updates the rows.
## Status legend
- `kept` — script exists and is called from somewhere outside its own directory.
- `unused?` — script exists but no caller was found. Could be dead, could be
intentional public API. Phase B triage decides.
- `missing` — referenced from code but no script file exists. Phase B triage
decides whether to port from Omarchy upstream, delete the caller, or stub
with `notify-send`.
- `port-from-omarchy` — Phase B verdict: lift the upstream Omarchy script,
rewrite for NixOS paths.
- `delete-dead` — Phase B verdict: remove and update callers.
- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub.
HEADER
# --- Render: scripts table ----------------------------------------------
printf '## Scripts (%d)\n\n' "${#script_loc[@]}"
printf '| Script | Location | Callers | Status | Notes |\n'
printf '| --- | --- | --- | --- | --- |\n'
# Sort scripts by name.
for name in $(printf '%s\n' "${!script_loc[@]}" | sort); do
local dir="${script_loc[$name]}"
local callers status callers_str
callers=$(ref_files_per_cmd "$name")
if [[ -z "$callers" ]]; then
status='`unused?`'
callers_str='—'
else
status='`kept`'
# Trim caller list to 2 entries + count.
local count
count=$(printf '%s\n' "$callers" | wc -l)
if (( count > 2 )); then
callers_str=$(printf '%s\n' "$callers" | head -2 | paste -sd, -)
callers_str="$callers_str, +$((count - 2)) more"
else
callers_str=$(printf '%s\n' "$callers" | paste -sd, -)
fi
fi
printf '| `%s` | `%s` | %s | %s | |\n' \
"$name" "$dir" "$callers_str" "$status"
done
echo
# --- Render: missing references -----------------------------------------
printf '## Missing references\n\n'
printf 'Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bin/`, `lib/` that have no matching script file.\n\n'
printf '| Token | Referenced in | Status |\n'
printf '| --- | --- | --- |\n'
while IFS= read -r token; do
[[ -z "$token" ]] && continue
[[ -n "${script_loc[$token]:-}" ]] && continue
local refs
self_pattern=$(IFS='|'; echo "${self_refs[*]}")
refs=$(grep -rlE "\\b${token}\\b" \
"${grep_includes[@]}" \
"${search_dirs[@]}" 2>/dev/null \
| grep -vE "^(${self_pattern})$" \
| sort -u)
[[ -z "$refs" ]] && continue
local count refs_str
count=$(printf '%s\n' "$refs" | wc -l)
if (( count > 2 )); then
refs_str=$(printf '%s\n' "$refs" | head -2 | paste -sd, -)
refs_str="$refs_str, +$((count - 2)) more"
else
refs_str=$(printf '%s\n' "$refs" | paste -sd, -)
fi
printf '| `%s` | %s | `missing` |\n' "$token" "$refs_str"
done <<<"$all_refs"
echo
# --- Render: menu items -------------------------------------------------
printf '## Menu items\n\n'
printf 'Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*_menu` function becomes one row.\n\n'
printf '| Submenu | Entry | Calls | Status |\n'
printf '| --- | --- | --- | --- |\n'
awk '
/^show_[a-z_]+_menu\(\) {/ { sub(/\(\) {/, ""); current=$1; in_func=1; next }
/^[a-z_]+\(\) {/ && !/^show_/ { current=""; in_func=0; next }
/^}$/ { current=""; in_func=0; next }
!in_func { next }
/^ case \$\(menu / {
# extract the menu title between the first pair of double quotes
match($0, /menu "[^"]+" "[^"]+"/);
if (RSTART == 0) next;
title=substr($0, RSTART, RLENGTH);
# second quoted string is the option list
n=split(title, parts, "\"");
title=parts[2];
options=parts[4];
# split options on \n
split(options, opts, "\\\\n");
pending_submenu=current;
pending_title=title;
for (i=1;i<=length(opts);i++) pending_opts[i]=opts[i];
pending_count=length(opts);
next
}
/^ \*[A-Za-z]/ {
# case arm — extract pattern between the first * and the closing )
match($0, /\*[^)]*\)/);
if (RSTART == 0) next;
arm=substr($0, RSTART, RLENGTH);
gsub(/[*)]/, "", arm);
gsub(/^[[:space:]]+|[[:space:]]+$/, "", arm);
# action follows the )
rest=substr($0, RSTART+RLENGTH);
sub(/^[[:space:]]+/, "", rest);
sub(/[[:space:]]*;;[[:space:]]*$/, "", rest);
# match the first nomarchy-* token in the action
cmd=""
if (match(rest, /nomarchy-[a-z0-9-]+/)) {
cmd=substr(rest, RSTART, RLENGTH);
}
printf "%s|%s|%s\n", pending_submenu, arm, cmd;
}
' features/scripts/utils/nomarchy-menu > /tmp/nomarchy-menu-rows.$$
while IFS='|' read -r submenu entry cmd; do
[[ -z "$entry" ]] && continue
[[ "$entry" =~ ^\) ]] && continue
status='`kept`'
if [[ -n "$cmd" ]]; then
if [[ -z "${script_loc[$cmd]:-}" ]]; then
status='`missing`'
fi
else
cmd='_(inline)_'
fi
printf '| `%s` | %s | `%s` | %s |\n' "$submenu" "$entry" "$cmd" "$status"
done < /tmp/nomarchy-menu-rows.$$
rm -f /tmp/nomarchy-menu-rows.$$
echo
}
out=""
if [[ "${1:-}" == "--out" ]]; then
out="${2:?--out needs a path}"; shift 2
fi
if [[ -n "$out" ]]; then
main >"$out"
else
main
fi

View File

@@ -33,3 +33,4 @@ else
fi
echo "Environment update complete."
nomarchy-hook post-update

View File

@@ -22,4 +22,8 @@ esac
hyprctl keyword misc:disable_scale_notification true
hyprctl keyword monitor "$ACTIVE_MONITOR,${WIDTH}x${HEIGHT}@${REFRESH_RATE},auto,$NEW_SCALE"
hyprctl keyword misc:disable_scale_notification false
# Persist the choice declaratively
nomarchy-state-write "hyprland.scale" "$NEW_SCALE"
notify-send -u low "󰍹 Display scaling set to ${NEW_SCALE}x"

View File

@@ -25,7 +25,6 @@ else
hyprctl keyword general:border_size 0
fi
TMP_JSON=$(mktemp)
jq --argjson state "$NEW_STATE" '.hyprland = $state' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write hyprland "$NEW_STATE" --type json
echo "Toggled gaps to $NEW_STATE declaratively."

View File

@@ -1,14 +1,16 @@
#!/bin/bash
set -e
# Check current single_window_aspect_ratio setting
CURRENT_VALUE=$(hyprctl getoption "layout:single_window_aspect_ratio" 2>/dev/null | head -1)
# single_window_aspect_ratio lives under the dwindle namespace (the default
# layout), not a bare `layout:` — `hyprctl getoption layout:…` returns
# "no such option" and the keyword set is silently dropped.
CURRENT_VALUE=$(hyprctl getoption "dwindle:single_window_aspect_ratio" 2>/dev/null | head -1)
# Parse vec2 output: "vec2: [1, 1]" or "vec2: [0, 0]"
if [[ $CURRENT_VALUE == *"[1, 1]"* ]]; then
hyprctl keyword layout:single_window_aspect_ratio "0 0"
hyprctl keyword dwindle:single_window_aspect_ratio "0 0"
notify-send -u low " Disable single-window square aspect ratio"
else
hyprctl keyword layout:single_window_aspect_ratio "1 1"
hyprctl keyword dwindle:single_window_aspect_ratio "1 1"
notify-send -u low " Enable single-window square aspect"
fi

View File

@@ -1,15 +1,19 @@
#!/bin/bash
set -e
# Toggle the layout on the current active workspace between dwindle and scrolling
# Toggle the tiling layout between Hyprland's two built-in layouts, dwindle
# and master. (The old version read `.tiledLayout` off `hyprctl
# activeworkspace -j` and switched to "scrolling" — but that field doesn't
# exist, so the read was always null, and "scrolling" isn't a real layout
# (`hyprctl layouts` only lists dwindle/master), so the toggle silently
# did nothing.)
ACTIVE_WORKSPACE=$(hyprctl activeworkspace -j | jq -r '.id')
CURRENT_LAYOUT=$(hyprctl activeworkspace -j | jq -r '.tiledLayout')
CURRENT_LAYOUT=$(hyprctl getoption general:layout -j | jq -r '.str')
case "$CURRENT_LAYOUT" in
dwindle) NEW_LAYOUT=scrolling ;;
dwindle) NEW_LAYOUT=master ;;
*) NEW_LAYOUT=dwindle ;;
esac
hyprctl keyword workspace $ACTIVE_WORKSPACE, layout:$NEW_LAYOUT
hyprctl keyword general:layout "$NEW_LAYOUT"
notify-send -u low "󱂬 Workspace layout set to $NEW_LAYOUT"

View File

@@ -8,12 +8,20 @@ if command -v walker >/dev/null 2>&1; then
# /dev/null. If they inherit stdout from a $(...) caller (e.g. nomarchy-menu
# doing `$(menu ...)`), bash waits for those fds to close on every return,
# which hangs the terminal after each menu selection.
if ! pgrep -x elephant > /dev/null; then
# Prefer the Home Manager systemd user services (programs.walker.runAsService).
# The manual fallbacks below MUST check the services first: the service runs
# the wrapped binary `.elephant-wrapped`, which `pgrep -x elephant` never
# matches — so the old check spawned a *second*, competing elephant on every
# menu invocation, racing the service for the socket. Only hand-start when no
# service and no process exists (e.g. walker used outside the HM service).
if ! systemctl --user is-active --quiet elephant.service 2>/dev/null \
&& ! pgrep -x elephant >/dev/null && ! pgrep -f elephant-wrapped >/dev/null; then
setsid uwsm-app -- elephant </dev/null >/dev/null 2>&1 &
disown
fi
if ! pgrep -f "walker --gapplication-service" > /dev/null; then
if ! systemctl --user is-active --quiet walker.service 2>/dev/null \
&& ! pgrep -f "walker --gapplication-service" >/dev/null; then
setsid uwsm-app -- walker --gapplication-service </dev/null >/dev/null 2>&1 &
disown
fi

View File

@@ -204,7 +204,13 @@ show_font_menu() {
}
show_setup_menu() {
local options=" Audio\n Wifi\n󰂯 Bluetooth\n󱐋 Power Profile\n System Sleep\n󰍹 Monitors"
local options=" Audio\n Wifi\n󰂯 Bluetooth"
# Power Profile drives powerprofilesctl, which only exists when
# power-profiles-daemon is running. Laptops force it off in favour of
# TLP and desktops don't enable it, so gate the entry rather than
# offer an action that dies with "command not found".
command -v powerprofilesctl >/dev/null 2>&1 && options="$options\n󱐋 Power Profile"
options="$options\n System Sleep\n󰍹 Monitors"
[[ -f ~/.config/hypr/bindings.conf ]] && options="$options\n Keybindings"
[[ -f ~/.config/hypr/input.conf ]] && options="$options\n Input"
options="$options\n󰱔 DNS\n Security"

View File

@@ -9,8 +9,20 @@ if (($# == 0)); then
fi
# Find the first notification whose 'summary' matches the regex in $1
notification_id=$(makoctl list | grep -F "$1" | head -n1 | sed -E 's/^Notification ([0-9]+):.*/\1/')
output=$(makoctl list)
if [[ -n $notification_id ]]; then
makoctl dismiss -n $notification_id
if [[ $output == "["* ]] || [[ $output == "{"* ]]; then
# JSON format (mako 1.8+)
# Structure is usually an array of notifications or a wrapper object
notification_id=$(echo "$output" | jq -r --arg pat "$1" '
if type == "array" then .
else .notifications // .data[0] // [] end
| .[] | select((.summary // .["summary-text"] // "") | contains($pat)) | .id' | head -n1)
else
# Human format (older mako)
notification_id=$(echo "$output" | grep -F "$1" | head -n1 | sed -E 's/^Notification ([0-9]+):.*/\1/')
fi
if [[ "$notification_id" =~ ^[0-9]+$ ]]; then
makoctl dismiss -n "$notification_id"
fi

View File

@@ -1,9 +0,0 @@
#!/usr/bin/env bash
set -e
# Nomarchy AUR Stub
# Informs the user that AUR is not applicable to NixOS.
notify-send -u critical "Nomarchy" "The AUR is an Arch Linux feature and is not available on NixOS. Try searching for the package in nixpkgs using 'nix search nixpkgs <name>'."
echo "Error: AUR is not available on NixOS."
exit 1

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
# Nomarchy Package Drop Script
# Alias for nomarchy-pkg-remove.
nomarchy-pkg-remove "$@"

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
# Nomarchy Package Install Script
# Alias for nomarchy-pkg-add for users coming from other distros.
nomarchy-pkg-add "$@"

47
features/scripts/utils/nomarchy-refresh-config Normal file → Executable file
View File

@@ -1,9 +1,13 @@
#!/bin/bash
set -e
# nomarchy-refresh-config: Restore a specific configuration file to its stock version.
# Usage: nomarchy-refresh-config <relative-path-to-config>
# Example: nomarchy-refresh-config hypr/hyprland.conf
# nomarchy-refresh-config: Restore a config file in ~/.config to its stock
# version. The pristine copies live in ~/.local/share/nomarchy/config (deployed
# by core/home/configs.nix), so this works on any system without needing the
# flake source tree on disk.
#
# Usage: nomarchy-refresh-config <relative-path-under-~/.config>
# Example: nomarchy-refresh-config fastfetch/config.jsonc
CONFIG_FILE=$1
@@ -12,34 +16,17 @@ if [[ -z $CONFIG_FILE ]]; then
exit 1
fi
# Determine source directory (where stock configs are stored)
# In Nomarchy, we deploy them via Nix, but we also keep a copy in local share for easy access
STOCK_DIR="$HOME/.local/share/nomarchy/themes" # Fallback if specific config isn't themed
# Wait, actually we should use the one from /etc/nixos if available
STOCK_BASE="/etc/nixos/nomarchy/core/home/config"
if [ ! -d "$STOCK_BASE" ]; then
# Fallback to local share if /etc/nixos is not available
STOCK_BASE="$HOME/.local/share/nomarchy/config"
fi
STOCK_BASE="${XDG_DATA_HOME:-$HOME/.local/share}/nomarchy/config"
SOURCE_FILE="$STOCK_BASE/$CONFIG_FILE"
DEST_FILE="$HOME/.config/$CONFIG_FILE"
DEST_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/$CONFIG_FILE"
if [ ! -f "$SOURCE_FILE" ]; then
# Try searching in features/ as well
STOCK_BASE="/etc/nixos/nomarchy/features"
# Find the file in features
SOURCE_FILE=$(find "$STOCK_BASE" -name "$(basename "$CONFIG_FILE")" | head -n 1)
fi
if [[ -n $SOURCE_FILE ]] && [[ -f "$SOURCE_FILE" ]]; then
echo "Refreshing $DEST_FILE from stock $SOURCE_FILE..."
mkdir -p "$(dirname "$DEST_FILE")"
cp "$SOURCE_FILE" "$DEST_FILE"
notify-send "Config Refreshed" "$CONFIG_FILE has been restored to defaults."
else
echo "Error: Stock configuration for $CONFIG_FILE not found."
notify-send -u critical "Error" "Stock configuration for $CONFIG_FILE not found."
if [[ ! -f "$SOURCE_FILE" ]]; then
echo "Error: Stock configuration for $CONFIG_FILE not found in $STOCK_BASE."
notify-send -u critical "Error" "No stock config for $CONFIG_FILE." 2>/dev/null || true
exit 1
fi
echo "Refreshing $DEST_FILE from stock $SOURCE_FILE..."
mkdir -p "$(dirname "$DEST_FILE")"
cp "$SOURCE_FILE" "$DEST_FILE"
notify-send "Config Refreshed" "$CONFIG_FILE has been restored to defaults." 2>/dev/null || true

View File

@@ -90,12 +90,18 @@ LOCK_FILE="${STATE_FILE}.lock"
# Validate JSON before moving
if jq empty "$TMP_FILE" 2>/dev/null; then
mv "$TMP_FILE" "$STATE_FILE"
# Sync to the Nix configuration while still holding the lock.
# This prevents desyncs where a newer JSON state is overwritten by an
# older Nix state during concurrent writes.
if [[ "$STATE_FILE" == "$HOME/.config/nomarchy/state.json" ]]; then
if command -v nomarchy-sync-nix-state >/dev/null 2>&1; then
nomarchy-sync-nix-state
fi
fi
else
echo "Error: Failed to generate valid JSON"
exit 1
fi
) 200>"$LOCK_FILE"
# Clean up lock file
rm -f "$LOCK_FILE"

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -e
# Nomarchy Nix State Sync
# Bridges the gap between runtime UI changes (state.json) and declarative
# Nix configuration (nomarchy-state.nix).
STATE_JSON="$HOME/.config/nomarchy/state.json"
NIX_STATE_FILE="/etc/nixos/nomarchy-state.nix"
# If state.json is missing, nothing to sync
if [ ! -f "$STATE_JSON" ]; then
exit 0
fi
# Determine destination.
# If we are in the repo, we sync to ./nomarchy-state.nix (dev mode).
# Otherwise, we sync to /etc/nixos/nomarchy-state.nix (real install).
if [ -f "flake.nix" ] || [ -d ".git" ]; then
DEST="./nomarchy-state.nix"
elif [ -w "/etc/nixos" ]; then
DEST="$NIX_STATE_FILE"
else
# Nowhere to sync to
exit 0
fi
# Generate the Nix attribute set from JSON.
# We use jq to extract the keys and format them as Nix assignments.
# Note: font in state.json maps to fonts.monospace in Nix.
{
echo "# DO NOT EDIT MANUALLY - Managed by Nomarchy scripts."
echo "# This file mirrors your UI choices (theme, font, etc.) into the declarative"
echo "# Nix configuration. It is imported by your home.nix."
echo "{ lib, ... }:"
echo "{"
echo " nomarchy = {"
# Core UI strings
echo " theme = lib.mkDefault \"$(jq -r '.theme // empty' "$STATE_JSON")\";"
echo " fonts.monospace = lib.mkDefault \"$(jq -r '.font // empty' "$STATE_JSON")\";"
echo " panelPosition = lib.mkDefault \"$(jq -r '.panelPosition // empty' "$STATE_JSON")\";"
echo " wallpaper = lib.mkDefault \"$(jq -r '.wallpaper // empty' "$STATE_JSON")\";"
# Hyprland layout (numbers)
echo " hyprland = {"
echo " scale = lib.mkDefault \"$(jq -r '.hyprland.scale // "auto"' "$STATE_JSON")\";"
echo " gaps_in = lib.mkDefault $(jq -r '.hyprland.gaps_in // 5' "$STATE_JSON");"
echo " gaps_out = lib.mkDefault $(jq -r '.hyprland.gaps_out // 10' "$STATE_JSON");"
echo " border_size = lib.mkDefault $(jq -r '.hyprland.border_size // 2' "$STATE_JSON");"
echo " };"
# Toggles (booleans)
echo " toggles = {"
echo " suspend = lib.mkDefault $(jq -r '.suspend // true' "$STATE_JSON");"
echo " screensaver = lib.mkDefault $(jq -r '.screensaver // true' "$STATE_JSON");"
echo " idle = lib.mkDefault $(jq -r '.idle // true' "$STATE_JSON");"
echo " nightlight = lib.mkDefault $(jq -r '.nightlight // false' "$STATE_JSON");"
echo " waybar = lib.mkDefault $(jq -r '.waybar // true' "$STATE_JSON");"
echo " };"
echo " };"
echo "}"
} > "$DEST.tmp"
# Final atomic move
mv "$DEST.tmp" "$DEST"

View File

@@ -19,8 +19,7 @@ else
notify-send -u low " Screensaver disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.screensaver = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write screensaver "$NEW_VALUE" --type bool
echo "Screensaver state set to $NEW_VALUE. Updating environment..."

View File

@@ -22,7 +22,6 @@ else
notify-send -u low " Top bar disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.waybar = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write waybar "$NEW_VALUE" --type bool
echo "Waybar state set to $NEW_VALUE."

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Nomarchy Full Update Script
# 1. Removes the installation pin (if present) to follow the rolling release.
# 2. Updates flake inputs (Nomarchy, nixpkgs, etc.) in flake.lock.
# 3. Performs a full system update (NixOS + Home Manager)
set -e
# Fast fail offline check for gum
if ! ping -c 1 -W 2 1.1.1.1 >/dev/null 2>&1; then
if command -v gum >/dev/null 2>&1; then
gum style --foreground 196 "You are offline. Cannot update."
sleep 3
else
echo "You are offline. Cannot update."
fi
exit 1
fi
# Detect the repository location
if [ -f "/etc/nixos/flake.nix" ]; then
REPO_DIR="/etc/nixos"
elif [ -f "/etc/nomarchy/flake.nix" ]; then
REPO_DIR="/etc/nomarchy"
else
echo "Error: Nomarchy flake repository not found in /etc/nixos or /etc/nomarchy."
exit 1
fi
CURRENT_REV=$(jq -r '.nodes.nomarchy.locked.rev // empty' "$REPO_DIR/flake.lock" 2>/dev/null)
REMOTE_URL="https://git.bemagri.xyz/bernardo/Nomarchy.git"
LATEST_REV=$(git ls-remote "$REMOTE_URL" HEAD 2>/dev/null | awk '{print $1}')
if command -v gum >/dev/null 2>&1; then
gum style --foreground 212 --border double --margin "1 2" --padding "1 2" "Nomarchy System Update"
if [[ "$CURRENT_REV" != "$LATEST_REV" && -n "$LATEST_REV" ]]; then
echo -e "An update for Nomarchy is available!\n"
echo -e "Current Revision: ${CURRENT_REV:0:7}"
echo -e "Latest Revision: ${LATEST_REV:0:7}\n"
if ! gum confirm "Do you want to download and apply the update now?"; then
echo "Update cancelled."
sleep 2
exit 0
fi
else
echo -e "\nYou are already up to date! (${CURRENT_REV:0:7})"
if ! gum confirm "Do you want to force a system rebuild anyway?"; then
echo "Cancelled."
sleep 2
exit 0
fi
fi
fi
echo "--- Starting Nomarchy Full Update ---"
# 1. Unpin the flake if it's currently pinned to the install commit
if grep -q "rev=" "$REPO_DIR/flake.nix"; then
echo "Removing installation pin to follow rolling release..."
sudo sed -i 's/?rev=[a-f0-9]*//g' "$REPO_DIR/flake.nix"
fi
# 2. Update flake inputs
echo "Updating flake inputs (this may take a moment)..."
sudo nix --extra-experimental-features 'nix-command flakes' flake update --flake "$REPO_DIR"
# 3. System update
nomarchy-sys-update
echo "--- Nomarchy Full Update Complete ---"
if command -v gum >/dev/null 2>&1; then
echo ""
gum style --foreground 82 "Update Applied Successfully!"
echo "Press any key to exit."
read -n 1 -s -r
fi

View File

@@ -16,18 +16,42 @@ if [ -z "$REPO_DIR" ]; then
exit 0
fi
# We use a simple logic: Check for flake updates periodically.
# Since this is run by Waybar, we should be careful with performance.
# Fast-fail if offline to avoid expensive nix commands.
if ! ping -c 1 -W 2 1.1.1.1 >/dev/null 2>&1; then
exit 0
fi
# For a quick check, we can see if there are newer versions available for nixpkgs
# by checking nix flake metadata on the repo.
# Get the current locked revision for Nomarchy
LOCKED_REV=$(jq -r '.nodes.nomarchy.locked.rev // empty' "$REPO_DIR/flake.lock" 2>/dev/null)
# Get current status
CURRENT_REV=$(nix flake metadata "$REPO_DIR" --json | jq -r '.lock.nodes.root.inputs.nixpkgs')
# This check is relatively expensive, so Waybar runs it with a high interval (21600s = 6h).
if [ -z "$LOCKED_REV" ]; then
# Could be named something else or missing, fallback to generic icon
echo ""
exit 0
fi
# Just return an icon if we are in a system that can be updated.
# In a real implementation, we could compare local flake.lock vs upstream if it's a git repo.
# For now, we'll return the update icon to show it's active.
# Check if we are pinned in flake.nix
if grep -q "rev=" "$REPO_DIR/flake.nix" 2>/dev/null; then
# We are pinned. An update implies unpinning and upgrading.
# We can just show the icon to encourage the user to update and unpin.
echo ""
exit 0
fi
echo ""
# Get remote latest revision
REMOTE_URL="https://git.bemagri.xyz/bernardo/Nomarchy.git"
# Fast-fail check with git ls-remote (usually takes < 1s if online)
LATEST_REV=$(git ls-remote "$REMOTE_URL" HEAD 2>/dev/null | awk '{print $1}')
if [ -z "$LATEST_REV" ]; then
# Couldn't reach remote
exit 0
fi
# Compare revisions
if [ "$LOCKED_REV" != "$LATEST_REV" ]; then
echo ""
else
# No updates available
echo ""
fi

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Nomarchy Time Sync Script
# Force sync system time using systemd-timesyncd
set -e
echo "Syncing system time..."
if ! sudo systemctl restart systemd-timesyncd; then
echo "Error: Failed to restart systemd-timesyncd. Check internet connection."
exit 1
fi
# Wait a moment for sync
sleep 2
echo "Current time: $(date)"
echo "Time sync complete."

View File

@@ -13,9 +13,15 @@ fi
# Use ix.io as the default pastebin
if [[ -n $1 ]]; then
url=$(curl -F "f:1=@$1" ix.io)
# --connect-timeout 5: fail fast if ix.io is unreachable
url=$(curl -s --connect-timeout 5 -F "f:1=@$1" ix.io || echo "OFFLINE")
else
url=$(curl -F "f:1=@-" ix.io)
url=$(curl -s --connect-timeout 5 -F "f:1=@-" ix.io || echo "OFFLINE")
fi
if [[ "$url" == "OFFLINE" ]]; then
echo "Error: Could not upload log. Check your internet connection."
exit 1
fi
echo "Log uploaded to: $url"

View File

@@ -1,6 +0,0 @@
#!/bin/bash
set -e
# Used by Voxtype waybar module to open config on right click
exec nomarchy-launch-editor ~/.config/voxtype/config.toml

View File

@@ -1,21 +0,0 @@
#!/bin/bash
set -e
# Install voxtype and configure it for use.
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
nomarchy-pkg-add wtype voxtype-bin
# Setup voxtype
mkdir -p ~/.config/voxtype
cp ~/.config/nomarchy/default/voxtype/config.toml ~/.config/voxtype/
voxtype setup --download --no-post-install
if nomarchy-hw-vulkan; then
voxtype setup gpu --enable || true
fi
voxtype setup systemd
nomarchy-restart-waybar
notify-send " Voxtype Dictation Ready" "Press Super + Ctrl + X to toggle dictation.\nEdit ~/.config/voxtype/config.toml for options." -t 10000
fi

View File

@@ -1,5 +0,0 @@
#!/bin/bash
set -e
nomarchy-launch-floating-terminal-with-presentation "voxtype setup model"
nomarchy-restart-waybar

View File

@@ -1,20 +0,0 @@
#!/bin/bash
set -e
# Remove voxtype and its configurations.
if nomarchy-cmd-present voxtype; then
echo "Uninstall Voxtype to remove dictation."
# Remove services
systemctl --user stop voxtype.service 2>/dev/null || true
rm -f ~/.config/systemd/user/voxtype*
systemctl --user daemon-reload
# Remove packages and configs
nomarchy-pkg-drop wtype voxtype-bin
rm -rf ~/.config/voxtype
rm -rf ~/.local/share/voxtype
else
echo "Voxtype was not installed."
fi

View File

@@ -1,10 +0,0 @@
#!/bin/bash
set -e
if nomarchy-cmd-present voxtype; then
voxtype status --follow --extended --format json | while read -r line; do
echo "$line" | jq -c '. + {alt: .class}'
done
else
echo '{"alt": "", "tooltip": ""}'
fi

View File

@@ -56,6 +56,15 @@ if [[ -n "$POSITION" ]]; then
nomarchy-state-write panelPosition "$POSITION"
fi
# 4. Select monitor scaling
echo "Step 4: Choose your monitor scaling"
echo "Recommended: 'auto' for most screens, '2' for 4K/HiDPI."
SCALE=$(gum choose "auto" "1" "1.25" "1.5" "2")
if [[ -n "$SCALE" ]]; then
nomarchy-state-write "hyprland.scale" "$SCALE"
fi
# Skip system-modifying steps in the Live ISO environment
if [[ "$USER" == "nixos" ]]; then
echo ""
@@ -90,4 +99,5 @@ nomarchy-state-write welcome_done true --type bool
rm -f "$HOME/.config/nomarchy/.first-run-done"
gum style --foreground 82 "Setup complete! Enjoy your Nomarchy experience."
nomarchy-hook post-install
sleep 3

View File

@@ -88,6 +88,25 @@
modules = [{
nomarchy.theme = lib.mkForce themeName;
nomarchy.wallpaper = lib.mkForce "";
nomarchy.apps = {
alacritty.enable = true;
btop.enable = true;
elephant.enable = true;
swayosd.enable = true;
walker.enable = true;
vscode.enable = true;
kitty.enable = true;
lazygit.enable = true;
tmux.enable = true;
};
home.packages = with pkgs; [
firefox
xfce.thunar
imv
mpv
fastfetch
chromium
];
}];
}).activationPackage;
allThemeVariants = pkgs.linkFarm "nomarchy-all-theme-variants"
@@ -169,14 +188,31 @@
home-manager.users.nixos = {
imports = [ ./features ];
home.stateVersion = "25.11";
nomarchy.apps = {
alacritty.enable = true;
btop.enable = true;
elephant.enable = true;
swayosd.enable = true;
walker.enable = true;
vscode.enable = true;
kitty.enable = true;
lazygit.enable = true;
tmux.enable = true;
};
home.packages = with pkgs; [
firefox
xfce.thunar
imv
mpv
fastfetch
chromium
];
# Live-ISO-only welcome. Pops a notification a few seconds
# after the graphical session is up and opens a terminal
# parked at the installer command, so the user never has to
# hunt for it. Force highres monitor detection as some
# hardware defaults to 1024x768.
# hunt for it.
wayland.windowManager.hyprland.extraConfig = nixpkgs.lib.mkAfter ''
monitor = , highres, auto, 1
exec-once = sh -c 'sleep 3; notify-send -u critical -t 0 "Welcome to Nomarchy" "Run \`sudo /etc/install.sh\` in the open terminal or \`--dry-run\` to preview."'
exec-once = sh -c 'sleep 4; alacritty --title "Nomarchy Installer" -e bash -lc "echo; echo \"Welcome to the Nomarchy live ISO.\"; echo; echo \" sudo /etc/install.sh # install\"; echo \" sudo /etc/install.sh --dry-run # preview only\"; echo \" sudo /etc/install.sh --resume # resume after an interrupt\"; echo; exec bash"'
@@ -221,6 +257,25 @@
home-manager.users.nomarchy = {
imports = [ ./features ];
home.stateVersion = "25.11";
nomarchy.apps = {
alacritty.enable = true;
btop.enable = true;
elephant.enable = true;
swayosd.enable = true;
walker.enable = true;
vscode.enable = true;
kitty.enable = true;
lazygit.enable = true;
tmux.enable = true;
};
home.packages = with pkgs; [
firefox
xfce.thunar
imv
mpv
fastfetch
chromium
];
};
}
];

View File

@@ -49,6 +49,14 @@
];
services.xserver.videoDrivers = [ "amdgpu" "radeon" "nouveau" "modesetting" "fbdev" "virtio" ];
# VM guest agents. Without these the virtio-gpu connector only advertises
# its 1024x768 fallback mode, so Hyprland's `monitor = , highres, …` rule
# picks that as the highest available resolution and the live session boots
# tiny. The agents let the SPICE/QEMU host negotiate a real resolution.
# (The installed system gets the same wiring via core/system/vm-guest.nix.)
services.spice-vdagentd.enable = true;
services.qemuGuest.enable = true;
environment.etc."install.sh" = {
source = ../installer/install.sh;
mode = "0755";
@@ -81,6 +89,13 @@
# Allow passwordless sudo for the live user
security.sudo.wheelNeedsPassword = false;
# The live accounts have empty passwords, but pam_unix rejects an empty
# password by default — which leaves hyprlock unsolvable if the idle
# timer locks the session (nothing you can type unlocks it). Allow null
# passwords for the locker so it can be dismissed with a bare Enter.
# Live-ISO only: installed systems must never accept empty unlocks.
security.pam.services.hyprlock.allowNullPassword = true;
# Override the upstream installer helpLine (says "NixOS", points nowhere
# useful for us). Shown on every TTY before login and again as the MOTD.
services.getty.helpLine = lib.mkForce ''

View File

@@ -257,9 +257,14 @@ check_environment() {
fi
success "UEFI boot detected"
# Find Nomarchy repo
# Find Nomarchy repo. Resolve symlinks: on the live ISO /etc/nomarchy is a
# symlink chain (/etc/nomarchy -> /etc/static/nomarchy -> /nix/store/…-source).
# `builtins.getFlake` (used below to load nixpkgs.lib for the state-schema
# eval) rejects a symlink path with "path '…-source' is a symlink" on Nix
# 2.31+, which aborted every real-hardware install at "Creating system
# configuration". Pass it the resolved store directory instead.
if [[ -d "/etc/nomarchy" ]]; then
NOMARCHY_REPO="/etc/nomarchy"
NOMARCHY_REPO="$(realpath /etc/nomarchy)"
elif [[ -d "$(dirname "$0")/.." ]] && [[ -f "$(dirname "$0")/../flake.nix" ]]; then
NOMARCHY_REPO="$(realpath "$(dirname "$0")/..")"
fi
@@ -1462,6 +1467,42 @@ generate_flake_config() {
impermanence_opt=$'nomarchy.system.impermanence.enable = true;\n nomarchy.system.impermanence.mainLuksName = "'"$_main_luks_name"$'";\n nomarchy.system.impermanence.user = "'"$USERNAME"$'";'
fi
# Multi-disk LUKS unlock. `nixos-generate-config` only emits a
# boot.initrd.luks.devices entry for the container that backs a
# fileSystems device — and every subvolume mount points at the main
# /dev/mapper/crypted_main. The extra drives in a multi-disk BTRFS array
# are members of that filesystem but are invisible through the
# device-mapper path it traces, so it never declares them. Without the
# entries below, initrd unlocks only the main drive, the multi-device
# BTRFS can never be assembled, and the installed system hangs at boot
# (the failure is invisible single-disk and only bites multi-disk
# hardware). We also re-assert the x-systemd.requires mount options disko
# set on every BTRFS mount (installer/disko-config.nix) but which
# nixos-generate-config drops — they make systemd-initrd wait for ALL
# members before mounting. Drive→mapper/partlabel naming mirrors
# disko-config.nix's `sanitize` (replace '/' and '-' with '_').
local EXTRA_LUKS_OPTS=""
local _ld=($TARGET_DRIVE)
if (( ${#_ld[@]} > 1 )); then
local _luks_lines="" _requires="" _i _extra _san _mapper _partlabel _uuid
for (( _i=1; _i<${#_ld[@]}; _i++ )); do
_extra="${_ld[$_i]}"
_san="${_extra//\//_}"; _san="${_san//-/_}" # /dev/sdb -> _dev_sdb
_mapper="crypted_${_san}"
_partlabel="disk-extra_${_san}-luks"
_uuid=$(blkid -s UUID -o value "/dev/disk/by-partlabel/$_partlabel")
_luks_lines+=$'\n'" boot.initrd.luks.devices.\"$_mapper\".device = \"/dev/disk/by-uuid/$_uuid\";"
_luks_lines+=$'\n'" boot.initrd.luks.devices.\"$_mapper\".allowDiscards = true;"
_requires+=" \"x-systemd.requires=/dev/mapper/$_mapper\""
done
# BTRFS subvolume mountpoints — must match installer/disko-config.nix.
local _mp _fs_lines=""
for _mp in / /persist /home /nix /var/log /.snapshots; do
_fs_lines+=$'\n'" fileSystems.\"$_mp\".options = [$_requires ];"
done
EXTRA_LUKS_OPTS="$_luks_lines"$'\n'"$_fs_lines"
fi
local PROFILE_SYSTEM_OPTS=""
local PROFILE_HOME_PACKAGES=""
@@ -1584,7 +1625,7 @@ FLAKE_EOF
imports = [
$HARDWARE_MODULES
];
$NOMARCHY_HW_OPTS
$NOMARCHY_HW_OPTS$EXTRA_LUKS_OPTS
}
EOF
@@ -1699,9 +1740,6 @@ $xkb_variant_line
# fcitx5 input method (CJK / IME).
# nomarchy.system.inputMethod.enable = true;
# voxtype voice-typing wiring (you must install voxtype yourself).
# nomarchy.system.voxtype.enable = true;
system.stateVersion = "25.11";
}
EOF
@@ -1714,6 +1752,12 @@ EOF
cat > /mnt/etc/nixos/home.nix << EOF
{ pkgs, ... }:
{
imports = [
# Machine-managed state (theme, font, toggles).
# UI scripts update this file automatically.
./nomarchy-state.nix
];
# Physical form factor — mirrors nomarchy.system.formFactor in system.nix.
# Gates UI affordances like the waybar battery widget.
nomarchy.formFactor = "$FORM_FACTOR";
@@ -1729,13 +1773,16 @@ EOF
# User-level packages (Home Manager).
#
# Nomarchy already ships a minimal desktop (firefox, thunar, mpv, imv, mako,
# hyprlock, swww, wl-clipboard, grim, slurp, rofi-wayland, etc.). The list
# below is a menu of extras — uncomment what you want and run
# \`nomarchy-env-update\`.
# Nomarchy already ships the core Hyprland ecosystem (mako, hyprlock, swww,
# wl-clipboard, grim, slurp, rofi, etc.). The list below includes the
# default user apps and a menu of extras — uncomment what you want and
# run \`nomarchy-env-update\`.
home.packages = with pkgs; [
# --- Enabled by default ---
btop # Resource monitor (TUI)
firefox
xfce.thunar
imv # Image viewer
mpv # Video player
fastfetch # System info at login
chromium # Secondary browser
$PROFILE_HOME_PACKAGES
@@ -1797,12 +1844,32 @@ EOF
# tldr
];
# --- Optional Nomarchy app modules ---
# --- Nomarchy app modules ---
# opencode AI coding CLI integration (deploys ~/.config/opencode/opencode.json).
# The \`opencode\` package itself is not installed automatically — add it to
# \`home.packages\` above if you want it on PATH.
# nomarchy.apps.opencode.enable = true;
# These enable Nomarchy's curated configurations for the respective apps.
# Most are enabled by default to give you a complete desktop experience.
# Enabling the module also installs the corresponding package.
nomarchy.apps = {
alacritty.enable = true;
btop.enable = true;
elephant.enable = true;
swayosd.enable = true;
walker.enable = true;
vscode.enable = true;
kitty.enable = true;
lazygit.enable = true;
tmux.enable = true;
# opencode AI coding CLI integration (deploys ~/.config/opencode/opencode.json).
# The \`opencode\` package itself is not installed automatically — add it to
# \`home.packages\` above if you want it on PATH.
# opencode.enable = true;
# Deploys themed config if you install the package (Ghostty is not in nixpkgs)
# ghostty.enable = true;
};
# --- Optional Nomarchy app modules ---
# Extra Home Manager modules go here (program configs, services, etc.).
}
@@ -1831,6 +1898,46 @@ EOF
state = schema.system // { timezone = \"$_state_tz\"; };
in builtins.toJSON state
" | nrun jq '.' > /mnt/etc/nixos/state.json
# nomarchy-state.nix — the declarative counterpart to state.json.
# Imported by home.nix so UI changes are solidified in code.
# Generated from lib/state-schema.nix's home block.
nix --extra-experimental-features 'nix-command flakes' eval \
--impure --raw \
--expr "
let
flake = builtins.getFlake \"$NOMARCHY_REPO\";
lib = flake.inputs.nixpkgs.lib;
schema = import \"$NOMARCHY_REPO/lib/state-schema.nix\" { inherit lib; };
s = schema.home;
in ''
# DO NOT EDIT MANUALLY - Managed by Nomarchy scripts.
# This file mirrors your UI choices (theme, font, etc.) into the declarative
# Nix configuration. It is imported by your home.nix.
{ lib, ... }:
{
nomarchy = {
theme = lib.mkDefault \"\${s.theme}\";
fonts.monospace = lib.mkDefault \"\${s.font}\";
panelPosition = lib.mkDefault \"\${s.panelPosition}\";
wallpaper = lib.mkDefault \"\${s.wallpaper}\";
hyprland = {
scale = lib.mkDefault \"\${s.hyprland.scale}\";
gaps_in = lib.mkDefault \${toString s.hyprland.gaps_in};
gaps_out = lib.mkDefault \${toString s.hyprland.gaps_out};
border_size = lib.mkDefault \${toString s.hyprland.border_size};
};
toggles = {
suspend = lib.mkDefault \${lib.boolToString s.suspend};
screensaver = lib.mkDefault \${lib.boolToString s.screensaver};
idle = lib.mkDefault \${lib.boolToString s.idle};
nightlight = lib.mkDefault \${lib.boolToString s.nightlight};
waybar = lib.mkDefault \${lib.boolToString s.waybar};
};
};
}
''
" > /mnt/etc/nixos/nomarchy-state.nix
}
# ============================================================================

View File

@@ -27,11 +27,11 @@
nightlight = false;
waybar = true;
# Hyprland window manager settings
hyprland = {
gaps_in = 5;
gaps_out = 10;
border_size = 2;
scale = "auto"; # Default monitor scale. Options: "auto", "1", "1.25", "1.5", "2"
};
};

View File

@@ -106,21 +106,44 @@ in
source = themeAppsPath + "/rofi.rasi";
};
# Walker fallback support
xdg.configFile."nomarchy/current/theme/apps/walker/style.css" = lib.mkIf (!builtins.pathExists (themeAppsPath + "/walker/style.css")) {
text = ''
* {
color: #${palette.base05};
}
#window {
# Dynamically generated CSS for Walker app launcher
xdg.configFile."nomarchy/current/theme/apps/walker/style.css".text = ''
#window {
background-color: #${palette.base00};
}
.item.active {
background-color: #${palette.base03};
color: #${palette.base0B};
}
'';
};
color: #${palette.base05};
border-bottom: 5px solid #${palette.base0D}; /* Accent */
border-radius: 15px;
}
#search {
background-color: #${palette.base01};
color: #${palette.base05};
border-bottom: 5px solid #${palette.base02};
border-radius: 10px;
margin: 15px;
padding: 10px;
}
#input {
color: #${palette.base05};
}
#list {
padding: 20px;
}
.item {
border-radius: 10px;
padding: 10px;
color: #${palette.base05};
}
.item.active {
background-color: #${palette.base02};
color: #${palette.base05};
border-bottom: 5px solid #${palette.base0D}; /* Accent */
}
'';
xdg.configFile."nomarchy/current/theme.name".text = config.nomarchy.theme;

View File

@@ -70,5 +70,8 @@ in
theme = lib.mkDefault "nomarchy";
};
boot.kernelParams = lib.mkDefault [ "quiet" "splash" "loglevel=3" "rd.systemd.show_status=false" "rd.udev.log_level=3" "udev.log_priority=3" "boot.shell_on_fail" ];
# Not mkDefault: kernelParams is a list nixpkgs and other modules add to at
# normal priority, so a mkDefault def is dropped — losing the quiet/splash
# boot. These params merge with (don't replace) the rest.
boot.kernelParams = [ "quiet" "splash" "loglevel=3" "rd.systemd.show_status=false" "rd.udev.log_level=3" "udev.log_priority=3" "boot.shell_on_fail" ];
}

View File

@@ -24,9 +24,8 @@ mkdir -p "$STATE_DIR"
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
if fc-list | grep -iq "$font_name"; then
TMP_JSON=$(mktemp)
jq --arg font "$font_name" '.font = $font' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
echo "Font set to $font_name declaratively. Applying changes..."
nomarchy-state-write font "$font_name"
echo "Font set to $font_name. Applying changes with nomarchy-env-update..."
if [[ "$no_update" == "true" ]]; then
echo "Skipping nomarchy-env-update due to --no-update flag."

View File

@@ -45,8 +45,7 @@ done
NEXT_INDEX=$(((INDEX + 1) % TOTAL))
NEW_BG="$BG_DIR/${BACKGROUNDS[$NEXT_INDEX]}"
TMP_JSON=$(mktemp)
jq --arg wp "$NEW_BG" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write wallpaper "$NEW_BG"
# Instant feedback via swww
if pgrep -x swww-daemon >/dev/null; then

View File

@@ -28,8 +28,7 @@ mkdir -p "$STATE_DIR"
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
# Persist the choice for the next rebuild.
TMP_JSON=$(mktemp)
jq --arg wp "$BACKGROUND" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write wallpaper "$BACKGROUND"
# Create symlink to the new background
ln -nsf "$BACKGROUND" "$CURRENT_BACKGROUND_LINK"

View File

@@ -34,13 +34,12 @@ if [ ! -d "$THEMES_DIR/$THEME_NAME" ]; then
exit 1
fi
TMP_JSON=$(mktemp)
jq --arg theme "$THEME_NAME" '.theme = $theme' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write theme "$THEME_NAME"
# Sync to system state if we have permissions (for system-level theming like browser policies)
SYSTEM_STATE_FILE="/etc/nixos/state.json"
if [ -w "$SYSTEM_STATE_FILE" ] || [ -w "/etc/nixos" ]; then
tmp=$(mktemp); sudo jq --arg theme "$THEME_NAME" '.theme = $theme' "$SYSTEM_STATE_FILE" > "$tmp" 2>/dev/null && sudo mv "$tmp" "$SYSTEM_STATE_FILE" 2>/dev/null || rm -f "$tmp"
nomarchy-state-write --file "$SYSTEM_STATE_FILE" theme "$THEME_NAME"
fi
# Try to find a background for this theme
@@ -48,8 +47,7 @@ BG_DIR="$THEMES_DIR/$THEME_NAME/backgrounds"
if [ -d "$BG_DIR" ]; then
BG=$(ls "$BG_DIR" | head -n 1)
if [ -n "$BG" ]; then
TMP_JSON=$(mktemp)
jq --arg wp "$BG_DIR/$BG" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write wallpaper "$BG_DIR/$BG"
fi
fi

View File

@@ -25,7 +25,6 @@ else
notify-send -u low " Nightlight disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.nightlight = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write nightlight "$NEW_VALUE" --type bool
echo "Nightlight state set to $NEW_VALUE."

View File

@@ -36,7 +36,10 @@ in
user = lib.mkDefault "nomarchy";
};
environment.systemPackages = lib.mkDefault [ nomarchy-sddm-theme ];
# Not mkDefault: systemPackages is a list every module contributes to at
# normal priority, so a mkDefault def is dropped entirely — leaving the
# SDDM theme package uninstalled and the greeter unthemed.
environment.systemPackages = [ nomarchy-sddm-theme ];
# Enable Hyprland system-level dependencies
programs.hyprland = {

View File

@@ -7,7 +7,7 @@ configuration {
modi: "drun";
show-icons: true;
icon-theme: "Papirus-Light";
terminal: "kitty";
terminal: "alacritty";
drun-display-format: "{name}";
case-sensitive: false;
location: 0;

View File

@@ -1,34 +0,0 @@
#window {
background-color: #fdf6e3;
color: #5c6a72;
border-bottom: 5px solid #dfa000;
border-radius: 15px;
}
#search {
background-color: #8da101;
color: #fdf6e3;
border-bottom: 5px solid #bdc3af;
border-radius: 10px;
margin: 15px;
padding: 10px;
}
#input {
color: #5c6a72;
}
#list {
padding: 20px;
}
.item {
border-radius: 10px;
padding: 10px;
}
.item.active {
background-color: #e6e2cc;
color: #5c6a72;
border-bottom: 5px solid #bdc3af;
}

View File

@@ -7,7 +7,7 @@ configuration {
modi: "drun";
show-icons: true;
icon-theme: "Papirus-Dark";
terminal: "kitty";
terminal: "alacritty";
drun-display-format: "{name}";
case-sensitive: false;
location: 0;

View File

@@ -1,34 +0,0 @@
#window {
background-color: #2d353b;
color: #d3c6aa;
border-bottom: 5px solid #d9bb80;
border-radius: 15px;
}
#search {
background-color: #a7c080;
color: #2d353b;
border-bottom: 5px solid #868d80;
border-radius: 10px;
margin: 15px;
padding: 10px;
}
#input {
color: #2d353b;
}
#list {
padding: 20px;
}
.item {
border-radius: 10px;
padding: 10px;
}
.item.active {
background-color: #3d484d;
color: #d3c6aa;
border-bottom: 5px solid #161a1d;
}