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>
9.5 KiB
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; for every option, 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 importscore/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:
# 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:
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 inthemes/engine/scripts/, notfeatures/scripts/utils/. All four dirs are built onto the user's$PATH, so at runtimenomarchy-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 withgrep -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)
- The active theme name lives in state:
~/.config/nomarchy/state.json(runtime) → mirrored into/etc/nixos/nomarchy-state.nixfor reproducibility. themes/engine/loader.nixreads that name and deploys the matching themed files (btop theme, kitty colors, waybar css…) into~/.config/.themes/templates/*.tplare filled with the palette's colors to produce dynamic configs.- 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, apreview/preview_type, andactions; or - a Lua file (
run = "lua:…") whoseGetEntries()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
.luafiles with a small bash generator that writes static elephant TOML menus (one[[entries]]per theme/wallpaper, each withpreview = "…/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 --dmenulike the other menus. Deletes both.luafiles, the elephant menu config for them, and thepkgs.luadependency. 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
--impureis required on rebuilds — the config reads runtime state (state.json) outside the flake.docs/SCRIPTS.mdanddocs/KEYBINDINGS.mdare 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.