Files
Nomarchy/docs/WORKING-ON-NOMARCHY.md
Bernardo Magri 83218a01f3
Some checks failed
Check / eval-and-lint (push) Failing after 6m21s
docs: add "Working on Nomarchy" orientation guide
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

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.systemcore/system/ (the OS)
  • nixosModules.homefeatures/ (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 behavesfeatures/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 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.