Compare commits

...

122 Commits

Author SHA1 Message Date
Bernardo Magri
802acfdc86 docs(roadmap): close out Pillar 8 / Component 9 (ISOs) + retire QA Now item
Component 9 (ISOs) closeout entry covers the two inline cleanups
(unreachable else branches in nomarchy-build-{iso,live-iso}) and the
regression-class verification: every tool install.sh calls is provided
by either the explicit host packages, the upstream profiles/base.nix
chain (gptfdisk, util-linux, kbd, systemd), or the nrun nix-run
fallback.

With Components 1–10 all shipped, the "Full QA audit" Now-column item
has done its job. Replaced it with a runtime-verification punch-list
entry that consolidates the "needs runtime verification" notes carried
forward by each component's closeout — boot the live ISO and walk
through waybar/menu/option-toggle paths on real hardware.
2026-05-21 21:14:30 +01:00
Bernardo Magri
1ae27cd302 chore(iso): drop unreachable else branches in build helpers
nomarchy-build-iso and nomarchy-build-live-iso both ran under set -e
but then wrapped nix build in an if [ \$? -eq 0 ] block with an else
that printed "Error: ISO build failed." and exit 1. set -e aborts the
script the instant nix build returns non-zero, so the else branch was
never reached — the user saw nix build's own error output and the
script exited.

Removed the dead conditional. Behaviour is identical.
2026-05-21 21:13:41 +01:00
Bernardo Magri
67e5cd6014 docs(roadmap): close out Pillar 8 / Component 8 + log setup-config UX issue
Component 8 (Scripts runtime behavior) closeout entry summarises the
four inline fixes: omacom URL in nomarchy-menu "Learn → Nomarchy",
looknfeel.conf path drift, unreachable Overrides case branch, and
schema-vs-script default drift for the theme fallback in
nomarchy-theme-bg-next.

New Later row tracks an interaction-pattern bug in
show_setup_config_menu: five of nine entries edit files that Home
Manager generates from declarative options, so the next rebuild
clobbers the edit. Two more open paths the modules don't deploy.
Either rewire the menu or surface the ephemerality.
2026-05-21 21:07:34 +01:00
Bernardo Magri
af2d9ffd8e fix(theme): align nomarchy-theme-bg-next default with the schema
The script's jq fallback for .theme was hardcoded to "nord", which
drifted from the lib/state-schema.nix default of "summer-night". With
no state.json (or a blank one), nomarchy-theme-bg-next would look up
backgrounds under themes/palettes/nord/ while the rest of the system
treated summer-night as active — a quiet inconsistency that produced
"no backgrounds found" errors on a system that hadn't yet written a
theme to state.

Matched it to the schema default. lib/state-schema.nix remains the
single source of truth.
2026-05-21 21:07:00 +01:00
Bernardo Magri
b078be3209 fix(menu): route Nomarchy help locally, fix looknfeel path, drop dead branch
Three issues in features/scripts/utils/nomarchy-menu:

(1) "Learn → Nomarchy" called nomarchy-launch-webapp on
https://learn.omacom.io/2/the-nomarchy-manual — an upstream Omarchy
URL, the same one fixed in nomarchy-manual on 2026-05-18. Now calls
nomarchy-manual, which opens the local ~/.local/share/nomarchy/
README.md (or notifies if the source tree isn't synced).

(2) "Style → Hyprland" tried to open ~/.config/hypr/looknfeel.conf,
which is not the path looknfeel.conf is deployed to. The actual file
lives at ~/.config/nomarchy/default/hypr/looknfeel.conf (sourced by
nomarchy.conf via the chain). Updated the path.

(3) The setup-config case statement had an *Overrides* branch but no
matching menu option, so it was unreachable. The overrides loader
(nomarchy.overrides.*) is still in the Next column of the roadmap;
when it ships, both the option AND the case will get added together.
2026-05-21 20:46:21 +01:00
Bernardo Magri
3a269d73eb docs(roadmap): close out Pillar 8 / Component 7 + correct templates row
Component 7 (Theme engine + palettes) closeout entry summarises the
three inline fixes (theme-set early-exit on missing dir; bg-set
state.json persistence + path validation; palette dead-surface
cleanup) plus the script-comment correction.

Updated the existing `themes/templates/*.tpl` Later row: the original
claim "no script in the tree consumes them" was wrong —
nomarchy-theme-set-templates does. Replaced with a concrete
categorisation: (a) functionally dead (alacritty/btop/chromium/swayosd
write to paths nothing reads), (b) superseded by Nix-side generators
(kitty/ghostty after 8d3ce2d), (c) still relevant
(hyprland/hyprlock/obsidian/keyboard.rgb plus the now-orphan
share-picker tpl to verify).
2026-05-21 20:42:33 +01:00
Bernardo Magri
fb4755fdbf docs(theme): correct nomarchy-themes-prebuild header comment
The header said "Run once after install (the installer wires this
up)". The installer doesn't actually wire it up — install.sh:1848
prints a final-screen tip telling the user to run it, but doesn't
invoke it (pre-realising every theme adds significant install time
and the user can opt in later). Corrected the comment to match.
2026-05-21 20:41:43 +01:00
Bernardo Magri
9b06b6c243 chore(palettes): remove dead per-palette files
Two clusters of palette-tree dead surface:

(1) themes/palettes/{flexoki-light,lumon,retro-82,rose-pine}/apps/
chromium.theme — 9-byte comma-separated RGB strings that no script
or Nix module reads. Chromium theming is driven by managed policies
in core/system/browser.nix (BrowserThemeColor / BrowserColorScheme
derived from the active palette's base00 + light-mode flag), not by
per-palette files. The chromium.theme files were vestigial from an
earlier chromium-theming mechanism.

(2) themes/palettes/summer-day/apps/kitty/{kitty.conf,everforest-
light.conf} — a 76KB stray kitty config in a nested apps/kitty/
subdirectory. features/apps/kitty/config/kitty.conf includes
~/.config/nomarchy/current/theme/kitty.conf (root of theme dir, not
under apps/), and themes/engine/files.nix now generates that file
from the active palette's base16 colors (commit 8d3ce2d). So the
summer-day file was at the wrong path AND superseded by the
generator.

`nix flake check --no-build` passes.
2026-05-21 20:39:38 +01:00
Bernardo Magri
893fa91fbf fix(theme): exit on missing theme; persist bg picks across rebuilds
Two related bugs in the theme switcher scripts:

(1) nomarchy-theme-set printed a warning when the theme directory didn't
exist but kept going — it wrote the bad name into state.json and ran
nomarchy-env-update on a broken state. Added an exit 1 after the
warning.

(2) nomarchy-theme-bg-set updated the live ~/.config/nomarchy/current/
background symlink + restarted swaybg but never wrote state.json. The
script is called by the walker background-selector menu
(elephant/nomarchy_background_selector.lua) and by the nomarchy-
wallpaper CLI wrapper, so every wallpaper picked via either path
silently reverted to the active theme's default on the next
home-manager switch — themes/engine/files.nix re-resolves
config.nomarchy.wallpaper at every rebuild. Now writes the chosen path
into state.json's wallpaper field, mirroring nomarchy-theme-bg-next.
Also added a file-exists check so a bogus path fails loudly instead of
leaving a dangling symlink + a failed swaybg process.
2026-05-21 20:35:52 +01:00
Bernardo Magri
4de8afbea9 docs(roadmap): close out Pillar 8 / Component 6 + log VSCode marketplace gap
Component 6 (Apps) closeout entry covers the three inline theming
fixes (kitty/ghostty per-palette colors, btop color_theme name match,
VSCode theme extensions split). One new Later row tracks the 15
palettes — including the default summer-night (sainnhe.everforest) —
whose theme extensions aren't in nixpkgs and need
pkgs.vscode-utils.extensionFromVscodeMarketplace per-palette pinning.

Chromium's static Default/Preferences symlink was confirmed redundant
with the managed-policy intent in core/system/browser.nix, but left
under the existing Later entry (the user already hedged on deletion
without chromium-internals confirmation).
2026-05-21 20:32:38 +01:00
Bernardo Magri
577b3aeb91 fix(vscode): always install palette theme extensions
programs.vscode.profiles.default.userSettings.workbench.colorTheme is
set unconditionally to the active palette's theme name (read from
themes/palettes/<theme>/apps/vscode.json), but the matching theme
extensions were bundled with devExtensions — which defaults to false.
So out of the box, VSCode silently fell back to the built-in dark
theme on every palette.

Split themeExtensions out as always-installed and devExtensions as
opt-in via nomarchy.vscode.devExtensions. themeExtensions covers the 6
palettes whose VSCode theme is packaged in nixpkgs (catppuccin,
catppuccin-latte, nord, tokyo-night, rose-pine, gruvbox).

The other 15 palettes (including the default summer-night, which uses
sainnhe.everforest) still break because their theme extensions are on
the VSCode marketplace but not yet in nixpkgs — handling that needs
pkgs.vscode-utils.extensionFromVscodeMarketplace plus per-palette
publisher/name/version/sha256 metadata. Logged separately.
2026-05-21 20:31:50 +01:00
Bernardo Magri
b52aec28ce fix(btop): point color_theme at the deployed nomarchy.theme
themes/engine/loader.nix:72 deploys the active palette's btop theme to
~/.config/btop/themes/nomarchy.theme, but btop.conf had
color_theme = "current" — btop looked for themes/current.theme, didn't
find it, and silently fell back to the built-in Default theme. So every
palette rendered btop in the same default colors regardless of the
selected Nomarchy theme.

Renamed the config reference to match the deployed file name.

lazygit and tmux both inherit terminal ANSI colors (verified: the tmux
status bar config uses blue/brightblack/etc., not hex), so the kitty +
ghostty + alacritty theming changes from 8d3ce2d cover them
transitively — no module fix needed.
2026-05-21 20:27:12 +01:00
Bernardo Magri
8d3ce2d841 fix(theme): generate per-palette kitty.conf + ghostty.conf
features/apps/kitty/config/kitty.conf:1 contains
  include ~/.config/nomarchy/current/theme/kitty.conf
and features/apps/ghostty/config/config:2 contains
  config-file = ?"~/.config/nomarchy/current/theme/ghostty.conf"

Neither file existed for any of the 22 palettes. The kitty include
failed silently and the ghostty include is optional (?-prefix), so both
terminals rendered with built-in default colors regardless of the
active Nomarchy theme.

Stylix has kitty.enable = true in themes/engine/stylix.nix but the
kitty module uses xdg.configFile rather than programs.kitty, so the
Stylix target had nothing to hook into. ghostty has no Stylix target at
all.

Generated both files from the active palette's base16 colors in
themes/engine/files.nix, mirroring the waybar.css pattern already there.
Color mapping reproduces the original colors.toml fields (background,
foreground, cursor, selection_*, color0..15) via base16 indices —
verified against the inverse mapping in themes/palettes/default.nix.

themes/palettes/summer-day/apps/kitty/kitty.conf (a 76KB stray file in
the wrong tree location) is unaffected by this fix — it was already
dead surface since the include path never resolved to it.
2026-05-21 20:26:13 +01:00
Bernardo Magri
0486e037df docs(roadmap): close out Pillar 8 / Component 5 + log hyprsunset finding
Pillar 8 Component 5 (Desktop stack) closeout entry covers the five inline
fixes from this sweep (hyprland apps.conf wiring, NNOMARCHY typo pair,
broken waybar palette overrides, dead bindings files, mako post-fix verify)
plus the doc reconciles (SCRIPTS.md, KEYBINDINGS.md). One new Later row
captures the nightlight/hyprsunset toggle-vs-systemd inconsistency — Nix
always enables the systemd unit with a rebuild-time temperature while the
toggle script pkills/exec's hyprsunset directly and hardcodes 4000K
instead of reading nightlightTemperature.

Runtime verification (boot live ISO; eyeball waybar across panel
positions × form factors × all 22 palettes, walker launcher modes, the
screensaver at idle) remains on the user before declaring Component 5
fully closed.
2026-05-21 20:19:31 +01:00
Bernardo Magri
40b62124e6 fix(scripts): correct \$NNOMARCHY_TOGGLE_* typos breaking 2 toggles
nomarchy-menu:330 and nomarchy-launch-screensaver:16 referenced
\$NNOMARCHY_TOGGLE_SUSPEND and \$NNOMARCHY_TOGGLE_SCREENSAVER with a
double-N. The real env vars injected by features/scripts/default.nix:69-73
are single-N. Both reads always resolved to the empty string, so:

  - nomarchy.toggles.suspend = false; still showed "Suspend" in the
    system menu (the condition is "!= false", so empty != false → true).
  - nomarchy.toggles.screensaver = false; still launched the screensaver
    on hypridle's 150s timeout (the gate "== false" never hit on empty,
    so the early-exit was skipped).

Both toggles documented in docs/OPTIONS.md were vacuous in practice.
2026-05-21 20:13:34 +01:00
Bernardo Magri
72443fd69f fix(waybar): drop 4 broken per-palette style.css overrides
themes/{catppuccin,lumon,nord,retro-82}/style.css fully replaced the
default style.css (no @import) but defined only 2–14 lines — just
@define-color declarations and, for nord, a minimal window#waybar block.
The default style ships ~110 lines covering #workspaces, #tray, #cpu,
#custom-nomarchy's Nomarchy-font override, margins/padding, the indicator
.active states, etc. So picking any of those four palettes produced a
waybar with zero structural styling.

The default style at features/desktop/waybar/config/style.css already
@imports ../nomarchy/current/theme/waybar.css — which themes/engine/
files.nix:30-34 generates with @background/@foreground/@accent from the
active palette. So removing the broken overrides restores per-palette
colors via the default-style path. summer-day and summer-night are kept
because their 100+-line style.css files are intentional, self-contained
visual redesigns (the case the themes/engine/loader.nix:76-79 comment
explicitly carves out for "themes that need significantly different
styling").
2026-05-21 20:07:25 +01:00
Bernardo Magri
641ab0cfb0 chore(hyprland): drop two dead bindings files in default/hypr/
bindings.conf was explicitly labeled "Deprecated bindings file. New
installations include everything directly." and was sourced by nothing.
plain-bindings.conf referenced \$terminal/\$browser/\$fileManager/\$music/
\$messenger/\$passwordManager — Hyprland variables that aren't defined
anywhere in the loaded config tree — and was likewise sourced by nothing.
Both files have been superseded by features/desktop/hyprland/config/
bindings.conf (the user-overrideable, mkDefault-deployed one at
~/.config/hypr/bindings.conf) and the entries already inside
default/hypr/bindings/{utilities,media,clipboard,tiling-v2}.conf.

Regenerated docs/SCRIPTS.md; the diff drops the stale plain-bindings.conf
callers and incidentally corrects four scripts whose Origin column was
out of date after they moved from core/system/scripts/ to
features/scripts/utils/ (nomarchy-env-update, nomarchy-pkg-add,
nomarchy-pkg-remove, nomarchy-preflight-migration).
2026-05-21 20:03:09 +01:00
Bernardo Magri
0297ec268f fix(hyprland): source the 9 unwired window-rule files in apps.conf
9 of the 17 .conf files under core/home/config/nomarchy/default/hypr/apps/
were deployed but never sourced. apps/system.conf carried the load-bearing
"tag +floating-window" rule that the bluetui/impala/btop/satty/screensaver
classes rely on, plus the "fullscreen, class:org.nomarchy.screensaver" rule
hypridle's 150s on-timeout depends on. apps/pip.conf carried the picture-
in-picture pin/float/size rules. Neither set of rules fired today — the
screensaver came up tiled, PiP windows didn't pin, and the floating-window
helpers shipped degraded.

Sourcing all 17 unconditionally; every rule is class- or title-gated so
the conditional ones (steam, qemu, 1password, etc.) no-op cleanly when the
app isn't installed — same pattern as the already-sourced telegram /
retroarch / localsend entries.
2026-05-21 20:00:30 +01:00
Bernardo Magri
72f7e7b93d docs(roadmap): log Hyprland keymap + waybar-toggle inconsistencies
Two behavioral wrinkles found during the Pillar 8 desktop-stack sweep
that need a design decision before they can be fixed. Logged as Later
rows so the audit doesn't lose them.

1. The Hyprland Wayland keymap is hardcoded to `us` in
   `core/home/config/nomarchy/default/hypr/input.conf:3`, ignoring the
   installer-chosen layout for native Wayland apps. Fix needs either a
   templated input.conf driven by a new home option, or session-level
   `XKB_DEFAULT_LAYOUT` propagation. Either path touches the installer
   heredoc and the home modules, so not a same-PR fix.

2. `nomarchy.toggles.waybar` is exported only as an env var consumed
   by the runtime toggle script. The Nix module always sets
   `programs.waybar.enable = lib.mkDefault true`, so the toggle is
   session-only — waybar comes back on every rebuild/reboot.
   Inconsistent with `toggles.idle` which correctly gates
   `services.hypridle.enable`. Needs a behavioral call (persistent
   gate vs intentional runtime-only with a clearer name).
2026-05-19 20:24:27 +01:00
Bernardo Magri
20de3d4f97 chore(hyprland): delete orphan config files + share-picker dir
Six unreferenced files surfaced under features/desktop/hyprland/config/
during the Pillar 8 sweep:

- `looknfeel.conf` and `autostart.conf` were deployed to ~/.config/hypr/
  but never sourced by nomarchy.conf. The substantive versions live in
  core/home/config/nomarchy/default/hypr/ and are sourced from there.
  Removed the deployment lines in features/desktop/hyprland/default.nix
  alongside the file deletes.
- `hyprlock.conf`, `hyprsunset.conf`, `xdph.conf` weren't deployed at
  all and weren't referenced anywhere. Pure leftovers.

The entire `features/desktop/hyprland-preview-share-picker/` directory
was also orphan: no `default.nix`, no Nix module imports the
`config.yaml`. Only mention was inside the (now-deleted) `xdph.conf`.
Deleted the directory.

No behavioral change — these files weren't being used. Just removes
dead surface that confuses contributors looking for the "real" config
location.
2026-05-19 20:24:16 +01:00
Bernardo Magri
2a301a049f fix(mako): deploy themed config to ~/.config/mako/config
`core/home/config/nomarchy/default/mako/core.ini` defines the Nomarchy
notification UX — urgency rules, app filters (Spotify silenced),
do-not-disturb mode, and button handlers for "Setup Wi-Fi" / "Update
System" / "Learn Keybindings" notifications. The file was deployed via
the bulk `nomarchy/` dir to
`~/.config/nomarchy/default/mako/core.ini`, but mako reads
`~/.config/mako/config` by default and `autostart.conf` launches it
without `--config`. So mako ran with stock defaults and the entire
themed UX was inert.

Added an explicit `xdg.configFile."mako/config".source` line in
core/home/configs.nix pointing at the existing themed file. mako now
picks up the Nomarchy rules out of the box.

Found during Pillar 8 audit of the desktop stack.
2026-05-19 20:24:06 +01:00
bd7e5a5706 Merge pull request 'wave/qa-core-system' (#3) from wave/qa-core-system into main
Reviewed-on: #3
2026-05-19 19:17:22 +01:00
Bernardo Magri
af8fa321ff docs(roadmap): log uwsm-in-virtualization module placement (Later)
core/system/virtualization.nix wires `programs.uwsm` + the Hyprland
session config at the top of the file — loaded unconditionally on every
install, with no actual relationship to libvirt/docker. Cosmetic
mislocation, not a behavior bug; logged as a Later row so it can be
fixed in a dedicated session module without growing this audit PR.

Found during Pillar 8 audit of core/system modules.
2026-05-19 19:13:47 +01:00
Bernardo Magri
6238f41e43 fix(hibernate): mkDefault on HandlePowerKey / IdleAction / IdleActionSec
These three settings.Login fields were set at default priority, so a
downstream system.nix that wrote (e.g.) `services.logind.settings.Login.HandlePowerKey = "poweroff"`
would collide with Nomarchy's value instead of overriding it. Same
mkDefault treatment as the other lid-switch settings in this block.

Found during Pillar 8 audit of core/system modules.
2026-05-19 19:13:23 +01:00
Bernardo Magri
fb4d5d7acc chore(schema): drop orphan features.makima
`lib/state-schema.nix` declared `system.features.makima = false` but
the field was never wired anywhere: no matching option in
core/system/options.nix, no consumer in core/system/state.nix, no
references in the wider tree. Schema-only ghost — removed.

Found during Pillar 8 audit of core/system modules.
2026-05-19 19:13:23 +01:00
Bernardo Magri
99a6c7d547 fix(impermanence): user must match created account, not hardcoded "nomarchy"
The persistence block at core/system/impermanence.nix:75 read
`users.nomarchy = { directories = [...]; }` — the username was a
literal, not a reference. For any user not literally named "nomarchy"
the block was silently inert and ~/.ssh, ~/.gnupg, ~/.local/share/keyrings,
Documents, Downloads, Pictures, Videos, Projects were wiped on every boot.

Adds `nomarchy.system.impermanence.user` (str, default "nomarchy") and
uses it via `users.${cfg.user}`. The installer now writes the chosen
username alongside `enable` and `mainLuksName` so impermanence installs
with non-default usernames are correct out of the box.

docs/OPTIONS.md: fixes the wrong path on the impermanence row
(documented `impermanence.enable`, real option is
`nomarchy.system.impermanence.enable`) and adds entries for
`mainLuksName` and `user`.

Found during Pillar 8 audit of core/system modules.
2026-05-19 19:13:23 +01:00
Bernardo Magri
85ef8745d7 chore: delete orphan config assets + log chromium/templates concerns
Two unreferenced asset files removed; two larger concerns deferred to
roadmap rows because they need more thought than a focused audit
allows.

Deleted:
- `features/apps/alacritty/config/alacritty.toml` — the alacritty
  module uses `programs.alacritty.settings` (Nix attrset) exclusively;
  nothing references the on-disk file. The neighbouring (already-empty)
  `themes/` directory goes with it.
- `themes/templates/mako.ini.tpl` — no script reads it.

Deferred to ROADMAP "Later":
- `features/apps/chromium/Default/Preferences` is deployed as a Home
  Manager symlink into chromium's mutable profile directory. Either
  silently replaced on first save or silently failing to write —
  either way the static defaults don't survive. The actual chromium
  theming work happens via managed policies in
  core/system/browser.nix. Needs chromium-internals knowledge to
  decide whether to remove or rework, so flagged rather than
  unilaterally deleted.
- `themes/templates/*.tpl` (the remaining 10 templates) are also
  apparently orphan — deployed via xdg.dataFile but unconsumed by any
  script. Likely vestigial from a pre-stylix templating system.
  Logged as a separate row to decide deletion vs documentation as
  user-reference assets.

Found during Pillar 8 audit of features/apps.
2026-05-19 19:04:56 +01:00
Bernardo Magri
b82954a7b5 fix(options): drop dead skipVsCodeTheme + 4 themeLoader.apps toggles
Two clusters of documented-but-non-functional options surfaced during
the Pillar 8 audit, both setting toggles that have zero runtime effect.

1. `nomarchy.toggles.skipVsCodeTheme` was declared in
   core/home/options.nix, defaulted from lib/state-schema.nix, and
   surfaced as `NOMARCHY_TOGGLE_SKIP_VSCODE_THEME` env var in
   features/scripts/default.nix — but `features/apps/vscode.nix` always
   sets `workbench.colorTheme` unconditionally, and no script reads the
   env var. Setting the toggle to true did nothing. Removed from
   options, schema, state, env-var export, and OPTIONS.md.

2. `nomarchy.themeLoader.apps.{waybar,mako,kitty,alacritty}` were
   declared in themes/engine/loader.nix but only `btop` is actually
   wired (line 87 gates the per-theme btop.theme deploy). The other
   four had no consumer. The actual theming pipeline for those apps is
   elsewhere: waybar themes inline from `colorScheme` in waybar.nix;
   kitty and alacritty are themed by stylix targets in
   themes/engine/stylix.nix; mako has no theme integration at all.
   Removed the four dead options + updated OPTIONS.md to list only
   btop with a note about where the other apps' theming lives.
2026-05-19 19:04:25 +01:00
Bernardo Magri
66c98949ab chore(features): drop orphan userPackages reader
`features/default.nix` had a let-block that read
`~/.config/home-manager/user-packages.json` at eval time via
`builtins.pathExists` + `builtins.readFile`, parsed it as JSON, and
filtered to valid pkgs — then never appended the result to
`home.packages` or anywhere else. The `userPackages` variable was
completely orphan.

Two problems with the dead code: (1) it was an undocumented hidden
mechanism (no docs mentioned `user-packages.json`), (2) it made flake
evaluation impurely depend on a user's home directory for no payoff —
flake outputs would silently differ between machines depending on the
presence of that file, even though nothing in the build used it.

Removed the let-block entirely. The nomarchyLib import stays.

Found during Pillar 8 audit of features/apps.
2026-05-19 19:04:25 +01:00
07e2d5c51c Merge pull request 'fix(home): remove dead behavior options, reserve overrides API' (#4) from wave/qa-core-home into main
Reviewed-on: #4
2026-05-19 18:49:22 +01:00
2529ca114f Merge branch 'main' into wave/qa-core-home 2026-05-19 18:49:15 +01:00
94927952db Merge pull request 'chore(lib): drop dead helpers, document schema boundary' (#2) from wave/qa-lib-schema into main
Reviewed-on: #2
2026-05-19 18:48:30 +01:00
0930458418 Merge pull request 'wave/qa-first-boot' (#1) from wave/qa-first-boot into main
Reviewed-on: #1
2026-05-19 18:48:02 +01:00
Bernardo Magri
95101fda3f fix(sddm): default autoLogin off, not on with hardcoded "nomarchy"
`themes/engine/sddm.nix` defaulted `services.displayManager.autoLogin`
to `enable = true; user = "nomarchy";` (both mkDefault). The installer
flow overrode both with the real username at normal priority, so this
was invisible there — but a hand-migrated user (per docs/MIGRATION.md)
who imported `nomarchy.nixosModules.system` without setting
`autoLogin.user` would auto-login as a nonexistent "nomarchy" user and
SDDM would error. `docs/MIGRATION.md` even documented the override as a
post-import chore.

Flipped the default to `enable = lib.mkDefault false`. Installer
generates `enable = true` directly so its flow is unchanged. Migration
flow now gets the safe default — opt-in instead of opt-out — and the
docs row is updated to reflect the new shape.

The hardcoded "nomarchy" username fallback for `autoLogin.user` is the
same class of bug as the impermanence persistence block was. A future
roadmap row to consolidate "primary user" across impermanence,
autoLogin, and any future modules might be worthwhile, but it's
deferred — this commit is the immediate fix.

Found during Pillar 8 audit of first-boot UX.
2026-05-19 18:46:41 +01:00
Bernardo Magri
6e0d17b859 fix(welcome): drop Step 4's dead starter home.nix generation
`nomarchy-welcome` wrote a "starter" `~/.config/home-manager/home.nix`
for users without one. Two problems:

1. Wrong path. The installer-generated canonical home.nix lives at
   `/etc/nixos/home.nix` and is imported via the flake (both
   home-manager.users and the standalone homeConfigurations). Nothing
   in the installer flow ever reads `~/.config/home-manager/home.nix`
   — it's a dead file.
2. Broken content. The starter is missing `home.username`,
   `home.homeDirectory`, `home.stateVersion`, and doesn't import
   `nomarchy.nixosModules.home`. Even on a hand-migration path it
   wouldn't evaluate as a standalone HM config.

So in the installer flow it's dead, and in the migration flow it's
broken. Removed Step 4 entirely. The git-init step (was Step 5) is
now Step 4. Hand-migrated users follow `docs/MIGRATION.md`, which has
the correct home.nix template.

Found during Pillar 8 audit of first-boot UX.
2026-05-19 18:46:30 +01:00
Bernardo Magri
27d1506b54 chore(lib): drop dead helpers, document schema boundary
Two unused helpers and a missing comment in the lib/ surface, found
during the Pillar 8 sweep.

- `readState` in `lib/default.nix` was exported but has no external
  callers — only `readHomeState` and `readSystemState` use it
  internally. Removed from the export list; the function stays in the
  let-block (still wraps the two public readers).

- `getWithDefault` in `lib/state-schema.nix` was a complete dead
  function: declared as a path-walking fallback helper but never called
  anywhere in the tree. core/{system,home}/state.nix use inline
  `togglesState.<key> or schema.<scope>.<key>` instead. Removed.

- Added a header comment to `lib/state-schema.nix` explaining the
  schema's boundary — it lists every state.json field consumed by a
  Nix option, but state.json may also hold runtime-only fields
  (`welcome_done` from `nomarchy-welcome`) that are intentionally
  off-schema because no Nix option reads them. Future readers will
  otherwise think welcome_done is an orphan.

Logged a Later-column roadmap row for consolidating `flake.nix`'s
palette/themeNames re-imports with `nomarchyLib` so the theme list has
one source of truth instead of two.
2026-05-19 18:28:54 +01:00
Bernardo Magri
90f07ae75c fix(home): remove dead behavior options, reserve overrides API
Two declared-but-non-functional option subsystems in core/home were
documented in OPTIONS.md and actively misleading users.

1. `nomarchy.behavior.hyprland.{bindings,input,windowRules,autostart}`
   were declared in core/home/behavior.nix with a `behaviorConfigs`
   mapping let-binding — both completely unread elsewhere in the tree.
   The actual hypr/*.conf files are deployed by
   features/desktop/hyprland/default.nix with `lib.mkDefault`,
   unconditionally. Setting `behavior.hyprland.bindings = false` had
   zero effect. OPTIONS.md's "Disable Nomarchy's default Hyprland
   keybindings" example was a lie. Removed the four dead options,
   deleted behavior.nix entirely, dropped the import from
   core/home/default.nix, and rewrote the OPTIONS.md example to use
   `xdg.configFile."hypr/bindings.conf".source = ./mine` (which
   actually works against the existing `lib.mkDefault` priority).

2. `nomarchy.overrides.{enable,paths}` advertised a file-based override
   loader that doesn't exist. The module created
   `~/.config/nomarchy/overrides/{hypr,waybar,apps}` directories and
   wrote a README claiming "place files here to override upstream
   defaults" — but `getOverrideOrDefault` was never called and `paths`
   was never populated. Rewrote core/home/overrides.nix to keep just
   the option declarations (so configs that already set these still
   evaluate) and marked them clearly as reserved/no-op in OPTIONS.md.
   Removed the misleading README write and dir-creation. Logged a
   Next-column roadmap row for implementing the loader properly.

While here:
- Clarified `nomarchy.configOverrides` (the *working* bulk-redirect
  mechanism) vs `nomarchy.overrides.*` (the reserved one) in OPTIONS.md
  — they're different things and the "See Overrides below" link was
  pointing at the broken subsystem.
- Fixed OPTIONS.md `nomarchy.iconsTheme` / `nomarchy.isLightMode`
  default text — both are derived from the active theme in
  core/home/state.nix, not the static literals the docs claimed.
- Updated docs/AGENT.md §2 and docs/STRUCTURE.md to reflect the
  behavior.nix removal and the overrides.nix reservation.

Found during Pillar 8 audit of core/home modules.
2026-05-19 18:08:58 +01:00
Bernardo Magri
bfd95cb40b docs(roadmap): mark Pillar 5 preset rows as Shipped
Laptop, Desktop, Accessibility, and Gaming presets all shipped on
2026-04-26 but were still tagged (Next). Reorders the pillar so the
two genuinely open items (dGPU auto-detect, Surface support) lead.
2026-05-19 17:28:16 +01:00
Bernardo Magri
9283403d8f docs(roadmap): add Pillar 8 QA audit + clean stale Pillar 4 rows
Pillar 3 audited script existence; Pillar 8 audits feature behavior.
Adds a per-component sweep methodology (10 components, one PR each on
wave/qa-<component>) and lists it on the Now board so the next session
can pick it up without re-deriving scope.

Cleanup pass on Pillar 4: removes two "(Now)" entries (software-profile
multi-select, form-factor → laptop preset) already in the Shipped log,
and promotes the two remaining open items ("What's installed?" summary,
non-LUKS branch) to the Now board.
2026-05-19 17:26:22 +01:00
Bernardo Magri
7bf4c3c637 fix(theme): set default to summer-night and fix scripts on live ISO
- Update lib/state-schema.nix to default both home and system themes to 'summer-night'.
- Fix 'nomarchy-theme-list' and 'nomarchy-theme-set-templates' to resolve themes and templates from '~/.local/share/nomarchy' instead of the obsolete '$NOMARCHY_PATH' (fixing failures on Live ISO).
- Update 'nomarchy-welcome' to properly convert Title Case theme display names back to kebab-case identifiers and add input validation to prevent crashes.
- Fix installer impermanence symlink by using a relative path ('../persist/etc/nixos'), ensuring it resolves during 'nixos-install' both inside and outside the chroot.
- Deploy '~/.XCompose' symlink via Home Manager and add 'nomarchy-restart-xcompose' to the menu.
- Relocate 'Nomarchy.ttf' to 'core/branding/' and move user-level scripts ('pkg-add', 'pkg-remove', 'env-update', 'preflight-migration') to 'features/scripts/utils/' to align with the distro architecture.
- Remove obsolete '$NOMARCHY_PATH' exports and redundant 'bashrc' template.
- Export theme templates via 'xdg.dataFile' for script accessibility.
2026-05-18 21:22:39 +01:00
Bernardo Magri
ec6046793e fix(installer): hardware-db references real modules + add ROG Ally + CI lint
Audited every entry in `installer/hardware-db.sh` against
`inputs.nixos-hardware.nixosModules` and found **21 of 43 entries (49%)
referenced modules that don't exist** in the upstream attribute set —
those installs would fail at eval time with "attribute not found"
errors on real hardware. Specifically:

  - Framework 13 per-gen: nixos-hardware uses `framework-11th-gen-intel`,
    not `framework-13-11th-gen-intel`. Fixed all four generations.
  - Framework 13 AMD AI 300: `framework-amd-ai-300-series` (no "13-").
  - Framework Intel Core Ultra: added `framework-intel-core-ultra-series1`.
  - Framework 16 AMD AI 300: added `framework-16-amd-ai-300-series`.
  - Framework generic fallback now uses the `framework` umbrella module.

  - ThinkPad X1 Carbon: modules are `lenovo-thinkpad-x1-Nth-gen`,
    not `-x1-carbon-genN`. Fixed gens 6/7/9/10/11; added X1 Nano.
  - ThinkPad P14s: requires arch+gen suffix; switched to the AMD gen3/4/5
    modules (the prior `lenovo-thinkpad-p14s` had no attribute).

  - Surface Pro 6/7/8/10: all share `microsoft-surface-pro-intel`. Pro 9
    keeps its dedicated module. Pro 3 fixed to `-pro-3`. Surface Book
    2/3 and Intel-based Surface Laptop 3/4/5: no nixos-hardware module
    — rows dropped; generic chassis+cpu+gpu detection still emits
    sensible `common-pc-laptop`.

  - ASUS ROG Strix G513 → `asus-rog-strix-g513im` (correct attr name).
  - ASUS ROG Zephyrus GA403 didn't exist — dropped. Added `ga402x`,
    `gu603h`, `g533zw`.
  - ASUS Zenbook generic `asus-zenbook-ux` was non-existent — dropped
    (too vague; available modules are per-model like `asus-zenbook-ux481`).

  - Dell Latitude 5400 / 7480: no modules — replaced with the existing
    `dell-latitude-7420`, `7430`, `7490`.

Added:

  - ROG Ally / Ally X support (`asus-ally-rc71l` for `RC71L`,
    `RC72LA`, and the "ROG Ally" product string). nixos-hardware
    currently ships one module for both revisions.

Documented (in a footer comment) the devices nixos-hardware doesn't
cover so they're known-unsupported rather than accidentally missing:

  - Valve Steam Deck → Jovian-NixOS as a separate flake input.
  - Snapdragon X laptops → aarch64 only; Nomarchy installer is x86_64.
  - Raspberry Pi → same as above.

Bug discovered along the way: the DB's pipe-separated row format
collides with bash regex alternation. A row like
`Microsoft|Surface Pro (10|8|7|6)|_|module` parses as 7 fields, with
"7" extracted as the module name. Surface Pro variants are now one
row per version.

CI gate added (`.forgejo/workflows/check.yml`): a new step extracts
every 4th-pipe-field from `HARDWARE_DB` and `comm -23`s it against
`inputs.nixos-hardware.nixosModules`. Any future entry pointing at a
non-existent module fails CI with a clear error. Closes the regression
class entirely.

Verified locally: bash -n + shellcheck --severity=error pass on
hardware-db.sh; the CI step's exact commands pass against the new DB.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:31:48 +01:00
Bernardo Magri
2b6d452509 fix: nomarchy-manual opens local README, not an Omarchy URL
The script hardcoded `xdg-open https://learn.omacom.io/2/the-nomarchy-manual`
— an upstream Omarchy page. Users hitting "Help → Manual" in nomarchy-menu
were sent to an unrelated site, and there's no nomarchy.org canonical
docs URL to point at instead.

Now opens `$HOME/.local/share/nomarchy/README.md`, which lives on every
installed system (per SKILL.md's "Out of Scope" note about
`~/.local/share/nomarchy/`) and links every doc in `docs/`. Falls back
to a notify-send "run nomarchy-update?" message if the source tree
isn't synced.

Pillar 6 entry in docs/ROADMAP.md updated to (Shipped).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:24:19 +01:00
Bernardo Magri
ac846f4b03 docs: STRUCTURE.md root listing + Pillar 6 reality-check
Two stale-doc cleanups in one commit. Both surfaced during the
post-Phase-B audit pass.

1. `docs/STRUCTURE.md` "Root Directory" listed three files that don't
   exist anywhere in the tree:
   - `GEMINI.md` (replaced long ago by `docs/AGENT.md`)
   - root-level `STRUCTURE.md` (this file actually lives in `docs/`)
   - `TODO.md` (long since replaced by `docs/ROADMAP.md`)

   Replaced with the actual root layout (flake.nix, flake.lock,
   README.md, .forgejo/, .githooks/) plus a `docs/` sub-tree that
   names every doc in the directory — the missing pieces the deleted
   bullets were trying to point at, now correctly located.

2. `docs/ROADMAP.md` Pillar 6 had three "Next" bullets that already
   shipped on 2026-04-26 (the welcome wizard, TROUBLESHOOTING.md, and
   the docs-index goal — README.md now links every doc in `docs/`).
   Moved all three to `(Shipped)`.

   Also rewrote the `nomarchy-manual` bullet — "orphaned reference
   today" was stale (the script is called from nomarchy-menu and
   nomarchy-theme-install per docs/SCRIPTS.md). The real remaining
   issue is its hardcoded `xdg-open https://learn.omacom.io/...` —
   an Omarchy URL that opens an unrelated upstream page when a user
   triggers the menu's Help entry. The bullet now names that
   specifically.

No code touched; doc-only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:21:36 +01:00
Bernardo Magri
03968e5d0d fix(installer): generate state.json from lib/state-schema.nix
Closes the last source-of-truth split after the state-defaults
centralization batches. The installer's heredoc was the only remaining
place that hardcoded the state.json literal — adding a default to the
schema previously required a parallel edit here, and silent drift was
exactly the bug class we kept fixing.

Before:

  cat > /mnt/etc/nixos/state.json <<JSON_EOF
  {
    "theme": "nord",
    "timezone": "${_state_tz}",
    "dns": "DHCP",
    ...
  }
  JSON_EOF

After:

  nix 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; };
      state = schema.system // { timezone = \"$_state_tz\"; };
    in builtins.toJSON state
  " | nrun jq '.' > /mnt/etc/nixos/state.json

Uses the flake's own pinned `inputs.nixpkgs` (matching what the rest of
Nomarchy resolves against), so the schema evaluates with the same `lib`
the consumer modules see. `nrun jq` pretty-prints for human inspection.

Behavioural notes:
- Output is identical to the old heredoc modulo alphabetical key
  ordering — `builtins.toJSON` sorts keys, the heredoc was in
  declaration order. Toggle scripts read/write via `jq` so it's
  invisible to them.
- Dry-run path unchanged. `execute_dry_run` already bind-mounts a fake
  /mnt for the generator; the generator's absolute paths still resolve.
- New schema fields show up automatically on the next install; no
  parallel edit needed.
- `bash -n` + `shellcheck --severity=error` clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:18:25 +01:00
Bernardo Magri
d264371b46 fix: complete hybridGPU wiring + make state-derived options overridable
Two related fixes that together close the "minimal wiring" gap behind
`nomarchy.system.features.hybridGPU`.

1. Complete the NVIDIA driver stack inside hardware.nix's hybridGPU
   mkIf block.

   Before: `hybridGPU = true` enabled supergfxd and... that was it.
   supergfxd manages mode switching by black/unblacklisting the nvidia
   kernel module, but without the rest of the NVIDIA stack actually
   loaded the dGPU has no driver to drive. Hyprland/Wayland silently
   stayed on the iGPU regardless of mode.

   After: hybridGPU=true also wires
     services.xserver.videoDrivers = ["nvidia"]   (loads the driver
                                                   under Wayland too)
     hardware.graphics.{enable,enable32Bit}
     hardware.nvidia.modesetting.enable           (required for
                                                   Wayland)
     hardware.nvidia.powerManagement.enable
     hardware.nvidia.package = config.boot.kernelPackages
                                      .nvidiaPackages.stable
     boot.kernelParams += "nvidia-drm.modeset=1"

   All wired with lib.mkDefault so a downstream system.nix can pin a
   beta driver, flip to the open kernel module, or set
   `hardware.nvidia.prime.{offload.enable, intelBusId, nvidiaBusId}`
   for render-offload. The bus IDs are per-machine (find via
   `lspci -D`) so they stay user-supplied; docs/OPTIONS.md has the
   full recipe.

2. Add lib.mkDefault to every state.json-derived assignment in
   core/system/state.nix and core/home/state.nix.

   Same priority bug on both sides: assignments like
   `features.hybridGPU = systemState.features.hybridGPU or false`
   landed at default priority. A downstream system.nix saying
   `nomarchy.system.features.hybridGPU = true` would then conflict
   with the state-derived value at the same priority, and Nix would
   refuse the merge with "conflicting definition values" — the
   user's override couldn't take effect.

   Verified by an explicit eval: extending the default nixosConfig
   with `nomarchy.system.features.hybridGPU = true` now resolves
   cleanly and the full driver stack engages.

   Side-effect: core/system/state.nix now reads from
   lib/state-schema.nix like the home side does, completing the
   schema-centralization started two batches ago.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:12:09 +01:00
Bernardo Magri
9c672953bc fix(installer): pre-flight resume polish (4 gaps)
Four resume-flow papercuts in `installer/install.sh` that hurt the
"interrupted install" path the most.

1. `--resume` with no state file is no longer silent.
   The most common operator confusion: reboot the live ISO, forget
   /tmp/ is tmpfs, re-run with --resume, watch the installer start
   over from scratch without saying anything. Now: loud error, tmpfs
   explanation, exit 1.

2. Validate the saved TARGET_DRIVE still exists on resume.
   Live ISO USB sticks get unplugged between sessions, dev hosts
   sometimes have non-deterministic /dev/sdX numbering. Without the
   guard the install proceeds and fails with cryptic disko / mount
   errors deep in execute_installation. Now we fail at load_state
   with the actual reason and a clean recovery path.

3. Resume now shows what's being resumed.
   `save_state` stamps an ISO-8601 timestamp; `load_state` prints
   "Resumed from <path> (saved Xm ago)" plus a "Target: /dev/X → user
   @ host" summary line. Lets the user Ctrl-C before any destructive
   prompt fires if they're resuming onto the wrong machine.

4. `--help` documents the tmpfs limitation.
   Saved state lives in /tmp/ which is tmpfs on the live ISO; --resume
   only works within the same boot. The man-page now says so instead
   of letting users discover it the hard way.

`format_age` is the one new helper — pretty-prints "Xs/Xm/Xh Ym/Xd"
relative to now, falls back to the raw timestamp if `date -d` can't
parse the input. shellcheck --severity=error passes.

Out of scope (potential future work):
- Persistent state across reboots (would need a writable USB / external
  drive — chicken/egg with the installer setting up the only persistent
  storage in the first place).
- `--show-state` flag to inspect a saved file without running.
- State-file schema versioning.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:00:02 +01:00
Bernardo Magri
7fa909ddf4 fix: centralize state defaults via lib/state-schema.nix
Kills a recurring bug class: state defaults previously lived in three
parallel places that drifted apart over time.

  - lib/state-schema.nix          (the canonical schema, referenced
                                   nowhere except a description string)
  - core/system/options.nix       (default = "..." clauses on options)
  - core/home/options.nix         (same, on home options)
  - core/home/state.nix           (`or "..."` fallbacks for state.json reads)

When `state.json` is missing a key, three files have to agree on the
fallback. They keep silently drifting:

  - The OOTB QA audit shipped fixes for this pattern.
  - Earlier this session, `chore: switch default theme summer-night → nord`
    fixed core/system/options.nix and core/home/state.nix — but missed
    core/home/options.nix, which still defaulted nomarchy.theme to
    "summer-night". Every consumer of the home option
    (features/default.nix, vscode.nix, waybar, hyprland, theme engine)
    resolved to the wrong theme when state.json was blank.

This change:

  - Imports lib/state-schema.nix into all three consumers and replaces
    every hardcoded default with `schema.<scope>.<key>`.
  - Fixes the lingering nomarchy.theme = "summer-night" home-side bug as
    a side-effect.
  - Touches roughly 25 literals across the three files.

Verified `nix flake check --no-build` passes and every centralized value
evaluates to the exact literal it previously had. Off-schema option-only
defaults (isLightMode, formFactor, cursor.*, iconsTheme, keyring.enable,
etc.) are left hardcoded — they have no state.json counterpart, so
there's no source-of-truth split to resolve.

Out of scope (follow-up):
  - Have installer/install.sh generate /mnt/etc/nixos/state.json from
    the schema instead of hardcoded JSON — would close the last
    split-brain surface (the installer can still drift from schema).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:52:47 +01:00
Bernardo Magri
5ddb15ffef ci: add Forgejo Actions workflow (eval + lint)
Pillar 7 first step. `.forgejo/workflows/check.yml` runs on every push
to main and every PR. Three sequential checks in one job:

1. `nix flake check --no-build`
   Catches eval regressions: broken option references, missing imports,
   stale module argument shapes. The same command AGENT.md tells humans
   to run by hand before declaring a change done.

2. `bash -n` + `shellcheck --severity=error` over every `nomarchy-*`
   bash script.
   Mirrors what `.githooks/pre-commit` does locally, but across the
   whole tree on every push — so a branch that bypasses the hook (via
   `--no-verify` or a fresh clone without `core.hooksPath` set) still
   gets gated. Severity is capped at error to match the hook; the long
   tail of style/info warnings can be cleaned up incrementally.

3. `docs/SCRIPTS.md` drift check.
   Regenerates the audit doc to a temp file and `diff`s against the
   committed version. Fails loudly with the fix command if a script
   add/remove/rename didn't include the regeneration step.

Dry-run results on the current tree:
- `nix flake check --no-build`: pass (only pre-existing warnings).
- shellcheck across 159 scripts at severity=error: pass.
- SCRIPTS.md drift: clean.

Activation:
Forgejo Actions isn't enabled on the repo yet, so the workflow lands
dormant. To activate: enable Actions on the repo in Forgejo's settings
and register a `forgejo-runner` on any Docker-capable Linux host. The
workflow uses `ubuntu-latest` and installs Nix itself via
`DeterminateSystems/nix-installer-action`, so no special runner image
is needed.

Deferred to a follow-up batch (needs binary cache infra):
- Building ISOs in CI (`nomarchy-installer`, `nomarchy-live`, default).
- Release pipeline (`vYY.MM.x` tags publishing ISOs as artifacts).
- `nixosTest` per palette with golden-image screenshot diffs.

`docs/STRUCTURE.md` now documents `.forgejo/` and `.githooks/` so future
agents and contributors can find both.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:34:05 +01:00
Bernardo Magri
46738c3663 chore(audit): final Phase B batch — close out unused? cluster
Pillar 3 Phase B, batch 4 (final). Triages the last 13 `unused?` rows:
five deletes and eight SKILL.md surfacings.

Deleted (no callers anywhere, work duplicated inline or marginal value):
- `nomarchy-restart-hyprctl` and `nomarchy-restart-mako`: stale comments
  claimed "used by theme switching" but no Nomarchy script calls them.
  Theme-set and refresh paths call `hyprctl reload` / `makoctl reload`
  directly (see nomarchy-refresh-hyprland).
- `nomarchy-restart-tmux`: 3-line pgrep+source-file wrapper. Users can
  `tmux source-file ~/.config/tmux/tmux.conf` themselves.
- `nomarchy-battery-present`: the battery monitor reads
  `/sys/class/power_supply/BAT*` inline; the helper never got wired in.
- `nomarchy-sudo-keepalive`: intended to be `source`d from longer-running
  scripts (nomarchy-update, etc.) but nothing sources it. Resurrect from
  git history if a future caller actually needs it.

Surfaced in SKILL.md (now tagged `kept` by the audit):
- Themes: `nomarchy-theme-{remove,refresh,bg-install}`
- System: `nomarchy-sudo-{passwordless-toggle,reset}`,
  `nomarchy-restart-trackpad` (intel_quicki2c THC reload — a real laptop
  bug fix worth documenting)
- New Virtualization section: `nomarchy-windows-vm {install,launch,stop,status}`
- Enriched Troubleshooting's generic `nomarchy-refresh-<app>` example with
  literal `nomarchy-refresh-fastfetch` so the audit catches it.

Verified `nix flake check --no-build` still passes and zero callers
reference the deleted scripts.

**Phase B is now complete.** Final audit state: 164 → 159 scripts, all
tagged `kept`, `unused?` = 0, missing references = 0. The audit table is
now a clean reference of what Nomarchy ships, not a triage backlog.
Logged in `docs/ROADMAP.md` Shipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:19:57 +01:00
Bernardo Magri
9be4363f4b chore(audit): triage webapp/tui/voxtype install-remove pairs
Pillar 3 Phase B, batch 3 — Batch A of the unused? clusters.

Deleted two dead webapp URI handlers:
- `nomarchy-webapp-handler-hey`
- `nomarchy-webapp-handler-zoom`

Neither was registered as a MimeType handler anywhere — a grep across
`*.desktop` files in `core/`, `features/`, `themes/`, `installer/`, and
`hosts/` returned zero matches. Without a `.desktop` registration the
system never routes `mailto:`/`zoom:`/`zoomus:` URIs to them, so the
handlers were unreachable code.

Kept the six remaining install/remove pairs (they're real CLI tools, just
unwired into any menu) and surfaced them in `SKILL.md` "Common Tasks" so
AI assistants can discover them on user request and the audit tags them
`kept`:
- Custom App Launchers: webapp-{install,remove,remove-all},
  tui-{install,remove,remove-all}
- Voice dictation: voxtype-{install,remove,status}

Menu-wiring these (e.g. a "Setup → Apps" submenu in nomarchy-menu) is a
separate Pillar 6 onboarding job, not scoped here.

Regenerated `docs/SCRIPTS.md` — script count 166 → 164, `unused?` 21 → 13.
Logged in `docs/ROADMAP.md` Shipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:13:39 +01:00
Bernardo Magri
f93eb7435f chore(audit): delete NixOS-irrelevant Omarchy port scripts
Pillar 3 Phase B, batch 2. Five `unused?` scripts that either duplicate
NixOS-native facilities or reference infrastructure Nomarchy doesn't
ship. All five had no callers anywhere in the tree.

- `nomarchy-rollback`: ran `nixos-rebuild rollback` after listing
  `snapper` snapshots. NixOS already exposes the previous generation in
  the boot menu and `nixos-rebuild --rollback`; Nomarchy uses
  impermanence, not snapper.
- `nomarchy-snapshot`: wrapped `snapper create/restore`. Same reason —
  snapper isn't part of Nomarchy. The script's "nomarchy-update can use
  this" comment never came true; nomarchy-update has no reference to it.
- `nomarchy-migrate-state`: one-time migration from old
  `~/.config/home-manager/state.json` and `/etc/nixos/state.json` to the
  unified `~/.config/nomarchy/state.json`. The installer now seeds the
  unified file directly; no current install needs the migration.
- `nomarchy-config-direct-boot`: added an EFI boot entry for a Nomarchy
  UKI. We don't build a UKI (no references anywhere in `core/` or
  `hosts/`), so the script targeted nonexistent infrastructure.
- `nomarchy-npx-install`: generated npx wrappers in `~/.local/bin/`. An
  Arch idiom — on NixOS the path is `nix-shell -p nodejs` or a
  declarative `home.packages` entry.

Kept `nomarchy-build-iso` and `nomarchy-build-live-iso` (the user-flagged
useful build wrappers) and surfaced them in README §2 in place of the
raw `nix build` command, which both removes the audit's `unused?` flag
on them and shortens the docs.

Regenerated docs/SCRIPTS.md (171 → 166 scripts; 28 `unused?` → 21).
Logged in docs/ROADMAP.md Shipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:08:09 +01:00
Bernardo Magri
6b2c678669 chore: switch default theme from summer-night to nord
Both the system option (`core/system/options.nix:theme`) and the home-side
state evaluator (`core/home/state.nix`) defaulted to "summer-night". The
installer-written state.json now seeds "nord" (see preceding installer
commit), and `lib/state-schema.nix` already defaults to "nord". Align the
hardcoded fallbacks here so a missing or blank state file lands on the
same theme everywhere instead of a now-inconsistent split.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:01:29 +01:00
Bernardo Magri
ac4d66e54d fix: nomarchy-sys-update targets actual hostname, not '#default'
The installer generates `nixosConfigurations.<hostname>` (see
installer/install.sh: `nixosConfigurations.$HOSTNAME`), but the system
update script was rebuilding `.#default` and using `--impure`. The
`#default` literal worked only on dev hosts that happened to be named
"default" and silently broke every toggle script on real installs.

Now resolves `$(hostname)` at runtime and aborts with a clear error if
empty. Dropped `--impure` — the flake doesn't need it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:01:22 +01:00
Bernardo Magri
098cd42ac8 fix(installer): harden multi-disk LUKS, password handling, revision pinning
Several installer reliability fixes that were left uncommitted:

- Impermanence + multi-disk LUKS: disko-config.nix names the main LUKS
  mapping `crypted` for single-disk and `crypted_main` once extraDrives is
  non-empty. The impermanence rollback hook used to hardcode `crypted`,
  which made every multi-disk install fail to mount root in initrd. Added
  a `nomarchy.system.impermanence.mainLuksName` option and wired the
  installer to write the correct value into the generated system.nix
  based on the drive count.

- Password no longer cleartext in /etc/nixos: installer now hashes the
  user password with `mkpasswd -m sha-512` and emits
  `initialHashedPassword` instead of `initialPassword`. Added mkpasswd to
  the live ISO. Cleartext is unset immediately after hashing.
  USER_PASSWORD_HASH is deliberately not persisted in --resume state —
  configure_user re-prompts on resume.

- Revision pinning that actually works on the live ISO: `inputs.self`
  strips .git in the Nix store copy, so `git rev-parse HEAD` would silently
  return empty on a real install and the generated flake would track main.
  Live ISO now writes `/etc/nomarchy-rev` from `inputs.self.rev` at build
  time; install.sh reads it first, falls back to git, and aborts with a
  loud confirmation prompt if both are empty (instead of silently
  installing an unpinned system).

- Generated `/mnt/etc/nixos/state.json`: toggle scripts (nomarchy-tz-select,
  nomarchy-setup-{fido2,fingerprint}, nomarchy-toggle-hybrid-gpu,
  nomarchy-wifi-powersave) `jq` this file in place and fail hard if it
  doesn't exist. Fresh installs now ship a schema-conformant file matching
  lib/state-schema.nix.

- Unmount /mnt before exiting `finish()` regardless of reboot choice. Clean
  unmount avoids dirty BTRFS on reboot; on "no", leaving /mnt mounted
  blocked a second installer run on the same live ISO.

- Removed obsolete `installer/disko-btrfs-luks.nix` (superseded by
  `disko-config.nix` per commit 3aadc36) and dropped its dangling
  `docs/STRUCTURE.md` reference.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:01:15 +01:00
Bernardo Magri
158ae308cc fix(audit): close all missing-references rows in script audit
The audit's "missing references" table held 15 rows — 2 real doc bugs and
13 grep false-positives — making Pillar 3 Phase B triage noisier than it
needed to be.

- Wrote themes/engine/scripts/nomarchy-theme-next so SKILL.md's documented
  "cycle to next theme" command actually resolves.
- Scrubbed three stale `nomarchy-dev-*` references from SKILL.md (skill
  frontmatter, body, and Out-of-Scope list) — they hallucinated a workflow
  that doesn't exist and broke AI-assisted use of the skill.
- Added a line-context filter to both nomarchy-docs-scripts generators
  that drops `nomarchy-*` tokens appearing in Nix pname/derivation idents,
  /tmp/ and /etc/sudoers.d/ paths, nixosConfigurations.* / packages.*
  flake outputs, mktemp -t prefixes, systemd unit vars, ./result/bin/run-
  binaries, and docker container references.
- Added a small token-level denylist for five residual non-script
  identifiers (nomarchy-plymouth, nomarchy-sddm-theme, nomarchy-live,
  nomarchy-rev, nomarchy-windows) that survive line filtering because
  they appear as bare Nix list refs, comment backticks, or compose-heredoc
  identifiers.

Regenerated docs/SCRIPTS.md; the "Missing references" section is now
empty. Logged in docs/ROADMAP.md Shipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 16:58:54 +01:00
Bernardo Magri
c1895eefd4 feat: implement Pillar 8: Distro Branding
Scrubbed remaining upstream references and solidified Nomarchy's identity:
- Replaced 'Omarchy' and 'Spirit of Omarchy' with brand-independent terms in README.md and scripts.
- Updated nomarchy-welcome banner to 'The Professional NixOS Desktop'.
- Set nomarchy-version codename to 'Sovereign'.
- Verified core/system/branding.nix for OS-release and bootloader labels.
- Verified SDDM and Plymouth metadata for correct branding.
- Updated ROADMAP.md board.
2026-05-04 22:08:27 +01:00
Bernardo Magri
74e2dc34e3 chore: complete OOTB QA audit
Fixes identified during the thorough distro review:
- Restore automatic wallpaper switching by removing image filters from deployed themes.
- Fix broken 'Style' menu entries by creating missing about.txt and screensaver.txt branding files.
- Clean up conflicting keybindings by removing deprecated tiling.conf and updating doc generator.
- Remove legacy Nord theme hack from nomarchy-theme-set.
- Fix JSON parse error in summer-day waybar theme.
2026-05-04 21:44:31 +01:00
Bernardo Magri
3510a51492 fix(installer): resolve multi-disk LUKS/BTRFS boot hang
- Move temporary LUKS keyfile to /tmp/ so Disko omits it from runtime config
- Explicitly add x-systemd.requires and x-systemd.device-timeout=0 to BTRFS mount options
- Ensures all LUKS devices are decrypted before BTRFS attempts to mount
2026-05-03 09:13:02 +01:00
Bernardo Magri
7064108ce7 fix(distro): fix /etc/nixos ownership, theme discovery, and CLI wrappers
- installer: set recursive ownership of /etc/nixos to main user post-install
- themes: fix NOMARCHY_PATH and discovery logic for Lua theme menu
- scripts: update CLI wrappers (font, theme, wallpaper) to use Walker menus
- core: remove obsolete NOMARCHY_PATH and cleanup dead code
- features: add pkgs.lua for Walker and remove obsolete switcher.nix
- docs: update ROADMAP.md, SCRIPTS.md and STRUCTURE.md
2026-05-03 08:59:13 +01:00
Bernardo Magri
bef7be01b8 fix(installer): wire HM as a NixOS module, move env-update to system layer
The post-install standalone HM activation kept failing in new ways
(daemon access, git ownership, missing PATH on first boot). Wire HM as
a NixOS module in the generated flake instead, so first-boot dotfiles
are activated by `nixos-install` itself with proper system context. The
standalone `homeConfigurations.<user>` is kept alongside for fast
iteration via `nomarchy-env-update`. Also:

- Drop the chroot HM activation block from the installer entirely.
- Move `nomarchy-env-update` from `features/scripts/utils/` to
  `core/system/scripts/` so it ships in `nomarchy-system-scripts` and
  exists on a freshly-installed system regardless of HM state.
- Set system-wide git `safe.directory` for /etc/nixos and the
  impermanence-relocated /persist/etc/nixos so the user-mode HM run
  doesn't trip on the root-owned flake repo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 12:24:12 +01:00
Bernardo Magri
d4f50afc62 fix(installer): start nix-daemon and trust flake repo for HM activation
HM activation inside `nixos-enter` failed with `big.lock: Permission
denied` because the chroot has no systemd and therefore no nix-daemon —
the user-level `nix run` fell back to single-user mode and couldn't
write /nix/var/nix/db. Launch nix-daemon manually for the activation
window and force NIX_REMOTE=daemon. Also mark /etc/nixos (and the
impermanence path) as a git safe.directory so HM doesn't trip over
git's dubious-ownership check on the root-owned repo. Make
nomarchy-env-update self-bootstrap via `nix run home-manager` when
home-manager isn't on PATH so the recovery hint actually works on a
freshly-installed system.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 11:56:22 +01:00
Bernardo Magri
2f18d4efcf fix(installer): unblock disko, bootloader, HM activation, hyprland res
- Pass --yes-wipe-all-disks to disko so the silent gum-spin path no
  longer hangs forever waiting on a hidden "yes" confirmation prompt
  (added in disko 1.13's destroy,format,mount mode).
- Stop threading an externally-built pkgs into the user flake's
  nixosSystem; configure nixpkgs through the module system instead so
  core/system/default.nix's nixpkgs.config.allowUnfree stops conflicting
  with the assertion "system configures nixpkgs with an externally
  created instance".
- Enable boot.loader.systemd-boot in the generated system.nix so the
  installed system has an actual bootloader (disko already lays out a
  1 GiB ESP at /boot).
- Bump nix.settings.download-buffer-size to 512 MiB to silence the
  "download buffer is full" warning on large NAR fetches.
- Activate home-manager via `runuser -l` instead of `runuser -u … --
  env HOME=…`. The latter only switches uid and leaves \$USER=root, so
  HM's activation script saw root, warned, and wrote dotfiles into
  /root/ — meaning the user's first login had no Hyprland config.
- Revert default Hyprland monitor line back to highres (live ISO and
  user default) — preferred falls back to EDID's 1024x768 in QEMU and
  on several laptop panels, which is the bug highres was put there to
  defeat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 11:07:02 +01:00
Bernardo Magri
329dc009b6 fix(installer): repair git init, LUKS reprompt, impermanence-no exit, quiet disko
- nrun git git init -q passed 'git' as a subcommand to git itself,
  failing the post-disko repo init. Drop the duplicated arg.
- disko's luks module reads passwordFile at the top level; placing it
  under settings.* meant it was silently ignored and disko fell back
  to askPassword=true, prompting the user again on luksOpen. Move the
  option to the right scope.
- configure_impermanence now uses local rc, nrun gum confirm, and an
  explicit case (0/1/130) with a final return 0 so a No answer no
  longer aborts the installer.
- run_disko_with_retry hides disko's chatty output behind a gum spin
  by default and surfaces the captured log on failure. Set
  NOMARCHY_VERBOSE_DISKO=1 to stream output live.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 09:48:37 +01:00
Bernardo Magri
0af1395df2 fix(themes): complete summer-day with Everforest Light palette
colors.toml and icons.theme were copy-pasted from summer-night (a dark
theme). Repalette to Everforest Light, mark as light via light.mode,
switch icons to Yaru-blue, fix the broken Waybar import, and add the
btop/neovim/vscode/rofi/walker app configs the sibling themes ship.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 09:48:21 +01:00
Bernardo Magri
f2b99e0f75 adding summer-day theme 2026-05-02 09:16:58 +01:00
Bernardo Magri
6411395d9f fix(qa): comprehensive out-of-the-box audit and repair
- Fix critical bash dynamic scoping bug in install.sh (Impermanence/Form Factor).
- Polished Live ISO with auto-login and passwordless sudo.
- Repurposed nomarchy-toggle-suspend to directly execute systemctl suspend.
- Updated nomarchy-launch-wifi to use nmtui in alacritty.
- Optimized nomarchy-welcome to avoid redundant rebuilds via --no-update flag.
- Enabled nomarchy-welcome in Hyprland autostart.
- Wrapped Live ISO-modifying steps in welcome wizard to prevent failures.
- Removed obsolete hardware auto-detection from nomarchy-on-boot.
- Hardened script doc generator against false-positive wildcard tokens.
- Regenerated docs/SCRIPTS.md and updated docs/ROADMAP.md.
2026-05-01 20:03:04 +01:00
Bernardo Magri
39b1a9c1b3 style(hyprland): set default monitor to 'preferred' resolution
Updated the default Hyprland monitor configuration to 'monitor=,preferred,auto,1'. This ensures the best resolution is selected automatically while maintaining a 1x scale by default.
2026-05-01 17:00:57 +01:00
Bernardo Magri
c5544e56c8 feat(system): default to latest stable kernel
Set boot.kernelPackages to pkgs.linuxPackages_latest by default. This ensures Nomarchy users benefit from the latest hardware support and security features in the mainline kernel.
2026-05-01 16:54:01 +01:00
Bernardo Magri
0306dff092 feat(installer): implement single-input flake architecture
- Refactor generated flake.nix to use the Appliance Model.
- Downstream flake now only defines the 'nomarchy' input.
- Dependencies (nixpkgs, home-manager) are inherited from nomarchy.inputs to ensure maximum stability and version alignment with upstream.
2026-05-01 16:51:53 +01:00
Bernardo Magri
3b977f181d fix(installer): resolve disko evaluation crash and infinite loops
- Fix disko-config.nix signature by adding '...' to handle unexpected CLI arguments.
- Update disko mode to 'destroy,format,mount' for the modern API and to avoid deprecation warnings.
- Fix infinite loops in 'configure_impermanence' and 'confirm_form_factor' caused by misinterpreting 'No' (rc=1) as an abort.
2026-05-01 16:43:05 +01:00
Bernardo Magri
61cd993e54 fix(githooks): skip bash linting on non-bash nomarchy-* scripts
The nomarchy-* prefix is a name convention, not a language guarantee:
nomarchy-haptic-touchpad is Python. Without a shebang filter, the
pre-commit hook would run `bash -n` on it and abort every commit
that touched the Python helper. Filter to scripts whose shebang
matches `bash` before linting; everything else passes through.

Found via the set -e sweep (1e94818) — the survey caught
nomarchy-haptic-touchpad as a "broken" bash script when it was
just non-bash.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:50:32 +01:00
Bernardo Magri
1e9481849b chore: add 'set -e' to every nomarchy-* bash script that lacks it
Sweep across the three script directories: features/scripts/utils,
core/system/scripts, themes/engine/scripts. 142 of 169 bash scripts
gained `set -e`; 27 already had it; the one Python helper
(nomarchy-haptic-touchpad) was skipped via shebang detection.

Why: bash's default behavior is to continue past a failed command,
which means a script that does "do A; do B; do C" leaves the system
in a half-applied state when B fails - and the user gets no signal.
Several recent fix commits (theme partial-apply, waybar reload race,
installer prewipe silent failures) all trace back to this. set -e
turns silent corruption into a loud abort the user can act on.

The 11 scripts with explicit `|| true` markers stay safe under set -e
because || true coerces the exit to zero; the markers continue to
mean "I deliberately tolerate this failure here."

Deliberate exception: nomarchy-menu runs WITHOUT set -e. It is an
interactive UX loop where action branches do `cmd; back_to <self>`
so a failed action would abort the script under set -e and the menu
would disappear without feedback. Soft-failure - menu re-displays,
user picks again - is the right semantic. Documented inline.

Validation: bash -n on every modified script (zero failures). The
new pre-commit hook (27f5663) was just updated to filter by shebang
so it doesn't try to bash-syntax-check the Python helper - that
filter was uncovered by this sweep.

Risk: set -e can surface latent bugs in scripts that previously
relied on silent continuation. If anything breaks, it's a real bug
that was already broken and is now visible. Easy per-script revert
if any UX glitches show up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:50:13 +01:00
Bernardo Magri
27f5663cdf chore(githooks): lint changed nomarchy-* scripts on commit
Adds two-tier linting before the existing docs/SCRIPTS.md regenerate
step:

- bash -n on every changed nomarchy-* script. Catches syntax errors
  that would otherwise be discovered at runtime by an unlucky user.
  Always fatal.
- shellcheck --severity=error when shellcheck is on PATH. Catches
  unquoted-var, use-before-define, missing-shebang, and other
  bug-shaped patterns. Only error-level issues block - the long
  tail of pre-existing warnings stays as a known cleanup task,
  not a commit blocker. Hook silently skips this step when
  shellcheck isn't installed (so contributors without it can still
  commit).

Catches the class of bug that's bit us repeatedly: a script ships,
the runtime path that exercises the broken line is rare, and the bug
sits latent until a user trips it. Cheaper to catch at commit time.

Caveat: 156 nomarchy-* scripts already have shellcheck warnings
(severity warning/info/style); we deliberately ship around them via
the --severity=error gate. A future per-script audit can dial the
severity up as scripts get cleaned up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:43:37 +01:00
Bernardo Magri
28cc41abdd fix(restart-app): wait for SIGTERM to take effect before respawning
Previous behavior: `pkill -x $1` (no wait) followed by an immediate
background `setsid uwsm-app`. The new instance attached its wayland
surface while the old one's surface was still mapped. Layer-shell
apps got the same visible ghosting that waybar showed on theme switch
before the SIGUSR2 fix (386da51), and non-layer apps got brief double
instances.

Fix:
- Quote $1 (was unquoted, breaks if app name has whitespace - rare
  but cost-free to fix while we're here).
- After SIGTERM, poll pgrep for up to ~1.5s in 100ms ticks.
- If anything is still alive after the poll window, SIGKILL it -
  prevents a misbehaving process from holding the surface forever.
- Only spawn the new instance after the old one is confirmed gone.

Affects every caller that hits the non-systemd-managed restart path
(menu's update-process actions, voxtype install/remove, font-change
follow-ups, etc.).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:09:15 +01:00
Bernardo Magri
5fc9f5ee34 fix(theme): surface partial-apply failures instead of swallowing them
nomarchy-theme-set chains six optional "tell each app the theme changed"
steps. Each used `command -v X && X || true`, which collapsed two very
different outcomes into the same silent path:

  - X isn't installed -> skip (correct, expected, fine)
  - X exists but returned non-zero -> skip (wrong - user just got a
    half-applied theme with zero feedback about which app didn't refresh)

Replaced the inline guards with a small helper that distinguishes
absent from failed and accumulates real failures into a list. At the
end of the run, if anything failed, we notify-send a single message
naming the apps that didn't refresh ("Did not refresh: Waybar, btop")
and echo the same to stderr. The theme apply itself still completes -
we don't abort the chain on one failure - so the user gets the partial
benefit AND the diagnostic.

Same pattern as the waybar SIGUSR2 fix (386da51): make the hot path
loud about real problems while staying quiet about expected
no-installed states.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:08:39 +01:00
Bernardo Magri
5c5b377bd6 fix(menu): quick-state actions return to their submenu instead of exiting
After the cancel-branch sweep, the remaining UX inconsistency was in
action branches: changing one setting kicked the user out of the menu,
forcing a relaunch to change the next. Brought 16 actions across 5
submenus into the same return-to-self pattern theme/background got.

Classification rule applied:

- Quick-state actions (toggle, set, restart-service - finishes in
  milliseconds, no window opens) -> back_to <self>, so the user can
  chain "toggle nightlight, then toggle gaps, then restart waybar"
  without rerunning nomarchy-menu each time.
- Window-opening actions (editor, floating terminal, audio/wifi/bt
  launcher, browser, hyprpicker overlay, screenshot, screenrecord,
  share dialogs, lock/shutdown/logout) stay as one-shot exits -
  re-popping the menu over the new window would be visual noise.

Submenus changed:

- show_toggle_menu (8 toggles): screensaver, nightlight, idle, top
  bar, workspace layout, window gaps, 1-window ratio, display scaling.
- show_setup_power_menu: powerprofilesctl set returns; cancel still
  goes up to show_setup_menu (different destinations on each branch,
  so the if/else stays).
- show_font_menu: nomarchy-font-set returns; cancel still goes up.
- show_setup_system_menu: the suspend toggle (quick) returns;
  hibernate enable/disable (terminal) still exit.
- show_update_process_menu (5 service restarts): hypridle, hyprsunset,
  swayosd, walker, waybar.

For dynamically-rendered menus (show_setup_system_menu rebuilds its
options each invocation based on current state) this also gives free
visual feedback - the toggle's label flips between "Enable Suspend"
and "Disable Suspend" when the menu re-renders.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:01:48 +01:00
Bernardo Magri
4b99fa3846 fix(menu): route every cancel branch through back_to for direct-keybinding consistency
Audit of all show_*_menu functions after the theme/background fix found
11 more cancel branches that called their parent directly instead of
back_to. None are reachable from current keybindings (today's direct
invocations target submenus that already use back_to), so the bug is
latent — but any future `nomarchy-menu <area>` keybinding into one of
these would bounce the user into the parent on Esc instead of exiting
cleanly, the exact bug that prompted the previous commit's fix to
show_theme_menu / show_background_menu.

Mechanical sweep:

  *) show_main_menu   ;;  ->  *) back_to show_main_menu   ;;   (5 sites)
  *) show_setup_menu  ;;  ->  *) back_to show_setup_menu  ;;   (3 sites)
  *) show_update_menu ;;  ->  *) back_to show_update_menu ;;   (3 sites)

Behavior under nested navigation (BACK_TO_EXIT=false) is unchanged:
back_to falls through to calling the parent function by name. Only
direct-invocation cancel paths gain the correct exit-0 behavior.

Action branches and go_to_menu's dispatch table intentionally still use
direct calls — those are forward navigation, not cancel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:53:41 +01:00
Bernardo Magri
a741b0936c fix(menu): theme and background submenus return to parent instead of exiting
The menu navigation contract: a submenu invoked directly via keybinding
(BACK_TO_EXIT=true, set by go_to_menu when nomarchy-menu is launched
with a target argument) should `exit 0` after the user's action; a
submenu invoked from a parent menu (BACK_TO_EXIT=false) should call
`back_to <parent>` to return where the user came from. back_to() honors
both modes.

Three submenus violated the contract:

- show_theme_menu and show_background_menu shell out to walker's
  Elephant plugin and don't call back_to. After picking a theme or
  wallpaper from Main -> Style -> Theme, the script exits silently
  instead of returning to Style; the user has to relaunch the menu
  from scratch to change anything else.

- show_hardware_menu's cancel branch called show_trigger_menu directly
  instead of back_to show_trigger_menu, which would have bounced a
  direct-keybinding caller into Trigger instead of exiting cleanly.

Adds the missing back_to call to the two walker-backed submenus
(parented to show_style_menu) and converts the hardware cancel branch
to back_to. The 16 other show_*_menu functions already conform.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:50:36 +01:00
Bernardo Magri
f318585dc4 fix(installer): harden disk selection and partitioning phase
The disk phase was the dominant source of incomplete installs. Six
concrete failure modes addressed in one pass:

1. Live-ISO USB excluded from the disk picker. select_disk previously
   filtered loop|ram|zram|sr but not the device the installer booted
   from; picking it would format the boot media mid-install. New
   detect_live_iso_devices walks /, /iso, /run/initramfs/live,
   /nix/.ro-store, /nix/store and resolves each backing device to its
   parent disk via lsblk -no PKNAME. Override with
   NOMARCHY_INSTALL_ALLOW_ISO_TARGET=1 for the developer case.

2. 10 GiB minimum-capacity preflight. Disko fails late and obscurely
   on undersized media; surface it while the picker is still open.

3. prewipe_target_drive rewritten:
   - Enumerates every active dm-crypt mapping via dmsetup ls and
     closes those whose backing device is on the target drive. The
     old version only knew about the hardcoded names "crypted" /
     "crypted_main" so an aborted multi-disk run or a non-Nomarchy
     install would leave a holder open and silently break the wipe.
   - Drops `|| true` from wipefs / sgdisk / dd. After the LUKS and
     swap teardown above, a real failure means something is still
     holding the device — surface that instead of papering over it.
   - udevadm settle bounded to 30s so a flapping USB can't hang.
   - Post-wipe sanity check: refuse to hand the disk to disko if
     anything is still mounted off it.

4. run_disko_with_retry wraps the disko call. On failure, shows the
   last 30 lines of output via gum style and offers Retry /
   View full log / Abort. set -e is suspended for the disko call so
   the exit code can be inspected. The previous bare `disko --mode
   disko` aborted the whole installer with output scrolled past.

5. Sed-templated disko-golden.nix + disko-btrfs-multi.nix pair
   replaced by a single disko-config.nix Nix function of
   { mainDrive, extraDrives ? [] } called via --argstr / --arg.
   Templating Nix via shell-escaped string substitution caused at
   least one production bug (3aadc36 fixed embedded-newline
   escaping); function arguments are the right shape and eliminate
   the entire class of escaping concerns. Single-disk path is
   `extraDrives = []`; multi-disk gets BTRFS `-d single -m raid1`
   plus the additional /dev/mapper/* devices. Hosts that shipped
   /etc/disko-golden.nix now ship /etc/disko-config.nix.

6. EXIT trap added so the tmpfs LUKS key file (/dev/shm/nomarchy-
   luks.key) is removed even if the script aborts between key-write
   and the explicit unset. Replaced redundant `shred -u` on tmpfs
   with `rm -f` (already in RAM).

Verification: bash -n on install.sh, nix-instantiate parse + strict
eval on disko-config.nix in both single and multi shapes, full
nix flake check --no-build evaluating all three NixOS configurations
(default, nomarchy-installer, nomarchy-live) plus the installerVm.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:42:00 +01:00
Bernardo Magri
386da51178 fix(waybar): SIGUSR2 reload to avoid surface-recreate ghost on theme switch
Theme-switching ran systemctl --user restart waybar.service, which tears
down waybar's wayland layer-shell surface and creates a new one
back-to-back. Hyprland needs a frame to clear the destroyed surface; the
new instance attaches its surface immediately, so for a frame or two the
old waybar pixels remain visible behind/under the new bar - the
"artifacts and old colors on top of new" symptom most visible on the
fresh compositor of the live ISO.

Switch to SIGUSR2 reload, which makes waybar re-read config.jsonc and
CSS (including @import-ed files like ~/.config/nomarchy/current/theme/
waybar.css that theme-switch rewrites) without destroying the surface.
Full systemctl start is kept for the cold-start case.

Drive-by: replace the `systemctl list-unit-files` presence check with
`systemctl cat` - list-unit-files returns 0 even on no-match, so the
old check would always pick the systemctl branch and never fall through
to the pkill fallback on systems where waybar isn't a systemd unit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:24:25 +01:00
Bernardo Magri
d06ef86bb9 feat(gaming): add nomarchy.gaming.enable home-side window rule
Mirror of nomarchy.system.gaming.enable. When on, injects a Hyprland
windowrulev2 = fullscreen, class:^(steam_app_).*$ so games launched
through Steam grab the whole screen instead of opening windowed.

Gated via lib.mkIf so the rule is absent when the option is off
(AGENT.md guardrail: features must be option-gated). The rule is
appended to wayland.windowManager.hyprland.extraConfig (types.lines)
so it composes cleanly with the existing source-line entry point in
features/desktop/hyprland/default.nix.

Closes the "Gaming - Hyprland window rule" Next-column roadmap row.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:14:21 +01:00
Bernardo Magri
3aadc36bff fix(installer): implement robust step-based navigation and fix multi-line sed error
- Implement a step-based state machine in main loop to support 'Back' navigation via Esc.
- Refactor all prompts to use safe exit-code capture (rc -eq 130/1) and handle 'not submitted' output.
- Add input flushing after Esc events to prevent cascading backtrack signals.
- Add short-circuit checks to every wizard stage for reliable skip-forward behavior.
- Fix sed error when generating multi-disk configurations by escaping newlines in additional_disks.
- Add explicit 'Set a hostname' message to the hostname prompt.
- Convert unsafe short-circuit lists to safe if statements to prevent set -e crashes.
2026-04-26 22:17:00 +01:00
Bernardo Magri
55f0653e59 feat(desktop): default to highest monitor resolution
- Change default monitor rule from 'preferred' to 'highres' in monitors.conf.
- Explicitly force 'highres' in the live ISO (nomarchy-live) to avoid low-res fallbacks on some hardware.
- Update roadmap.
2026-04-26 20:03:46 +01:00
Bernardo Magri
dd48411013 feat(scripts): extend nomarchy-welcome into a guided wizard
- Added nomarchy.panelPosition option and state persistence.
- Updated Waybar to respect the panelPosition setting.
- Refactored nomarchy-welcome to use state.json instead of a flag file.
- Added prompts for theme, font, panel position, and starter home.nix generation.
- Updated documentation and roadmap.
2026-04-26 20:02:52 +01:00
Bernardo Magri
c66f0b19cd feat(installer): add multi-disk BTRFS support
- Allow selecting multiple drives in the TTY installer using gum choose --no-limit.
- Add installer/disko-btrfs-multi.nix template for BTRFS RAID/Single setups.
- Dynamically generate multi-disk disko configurations with LUKS-on-every-disk.
- Default to BTRFS 'single' data and 'raid1' metadata for maximum capacity across mismatched drives (e.g., 20GB + 120GB SSDs).
- Update roadmap and structure documentation to reflect the new capabilities.
2026-04-26 19:44:34 +01:00
Bernardo Magri
6de8ecd093 feat(distro): rename ISO targets and fix UEFI boot in live test script
- Rename installerIso and installerIsoGraphical to nomarchy-installer and nomarchy-live.
- Update host configurations with proper Nomarchy branding and volume IDs.
- Fix nomarchy-test-live-iso QEMU launch by using -drive if=pflash for UEFI firmware.
- Add nomarchy-build-live-iso utility script.
- Scrub remaining Omarchy references in Plymouth, installer messages, and docs.
- Regenerate docs/SCRIPTS.md to reflect new and renamed utilities.
2026-04-26 15:29:04 +01:00
Bernardo Magri
21230a05eb feat(installer): review-then-edit loop with field-level re-prompt
Previously the review screen only offered Confirm/Abort, so a typo or
wrong-disk choice meant aborting the whole run and starting over (or
hand-editing /tmp/nomarchy-install.state.sh). On --resume the situation
was worse: every prompt re-runs (each short-circuits when its var is
set), the user lands on a review they can't change.

review_configuration() now offers Continue / Edit a field / Abort. Edit
opens a multi-select of every saved field; chosen fields clear and the
next loop iteration in main() re-prompts only those. The LUKS passphrase
short-circuits when already set, so editing other fields doesn't
re-prompt for it.

Net flow change:
- Fresh install: same prompts, then review with Edit option (typo fixes
  without restarting).
- --resume: state loads, every prompt skips (vars set), lands straight on
  review — exactly what the roadmap entry called for.

Verified via `bash -n`. Live VM dry-run not exercised in this session.
2026-04-26 09:21:40 +01:00
Bernardo Magri
4b2f16c2f0 docs: add TROUBLESHOOTING.md for the five common rebuild errors
Covers: option-already-declared (duplicate mkOption), attribute-missing
(forgot to import nomarchy.nixosModules.system), Stylix target conflict
(needs lib.mkForce, not bare bool), home-manager .hm-bak churn (left over
from backupFileExtension after first install), and impermanence path
missing (dir not in environment.persistence list).

Each entry has the literal error text, the cause, and a copy-paste fix.
Linked from README.md and docs/MIGRATION.md so users hit it before
guessing.
2026-04-26 09:16:40 +01:00
Bernardo Magri
21ee9c6035 feat(system): add gaming preset module
Opt-in `nomarchy.system.gaming.enable` (default false). Wires
`programs.steam` (with `remotePlay` and `localNetworkGameTransfers`
firewall holes opened via `mkDefault`), `programs.gamemode` (the
launching user must be in the `gamemode` group), and
`services.flatpak`.

Two pieces of the original roadmap entry split into separate
Next-column rows so the system-side preset ships now:

  1. Hyprland fullscreen-on-Steam-launch window rule (home-side).
  2. Declarative flathub remote (nixpkgs has no API for this; needs
     either an overlay or a one-shot systemd unit).

The flatpak service is enabled but the user must add flathub
manually after first boot — documented in OPTIONS.md.
2026-04-26 09:10:52 +01:00
Bernardo Magri
8266dc7ee2 feat(system): add accessibility preset module
Opt-in `nomarchy.system.accessibility.enable` (default false —
accessibility is a personal preference, not hardware-derived). Wires
`services.gnome.at-spi2-core`, installs `pkgs.orca`, and sets
`XCURSOR_SIZE` to a configurable `accessibility.cursorSize` (default
32, up from NixOS's 24).

The original roadmap entry bundled Hyprland-side bits (slower
key-repeat, Orca launch keybinding, high-contrast palette). Those
require touching home-manager / theme files and a new palette
directory; split into a separate Next-column row so the system-side
preset ships now and the desktop integration follows independently.
2026-04-26 09:06:02 +01:00
Bernardo Magri
16ed8f1df1 docs(agent): require docs to ship with the change that triggers them
Adds an 8th guardrail and replaces §5.4 with an explicit "if you change
X, update Y" mapping covering options, scripts, keybindings, structure,
installer, themes, roadmap, conventions, and flake-level changes.

Each row names the doc to touch. The closing line forces a one-pass
check before declaring a change done — eliminates "docs catch-up" PRs
and keeps the distro and its docs from drifting apart.
2026-04-26 08:53:58 +01:00
Bernardo Magri
e9c9342965 feat(system): add desktop preset module
Mirror of the laptop preset for the desktop form factor. New
`nomarchy.system.desktop.enable` defaults to `formFactor == "desktop"`,
so the installer's existing formFactor write auto-flips it on without
installer changes (same pattern as laptop).

The module pins `powerManagement.cpuFreqGovernor` to `"performance"`
(via mkDefault) and enables `services.zfs.{autoScrub,trim}` so a
future ZFS pool gets sensible maintenance for free. The ZFS knobs are
no-ops until the user adds zfs to `boot.supportedFilesystems`.

Battery widget filtering is already driven by `formFactor` itself in
`features/desktop/waybar/default.nix`, so the preset doesn't repeat
it. Closes the "Desktop preset module" Next item.
2026-04-26 08:51:28 +01:00
Bernardo Magri
5b014cfa29 chore(audit): refine docs-scripts detector and lock in via pre-commit
Two detector bugs fixed:

1. grep_includes missed *.lua, *.ini, *.desktop, *.json — so callers in
   elephant providers (lua), mako on-button-* hooks (ini), and any future
   MimeType-registered URL handlers (.desktop) were invisible. Adding them
   reclassifies nomarchy-notification-dismiss and nomarchy-theme-bg-set
   from `unused?` to `kept` (true callers in mako/core.ini and the
   elephant background_selector lua).

2. The all_refs regex `nomarchy-[a-z0-9][a-z0-9-]+` greedily captured
   trailing dashes, producing junk missing-tokens like `nomarchy-pkg-`,
   `nomarchy-cmd-`, `nomarchy-restart-`, etc. from glob references like
   `for c in nomarchy-pkg-*`. Tightened to require an alphanumeric end
   character. Also restricted to grep_includes so the binary tmpfile
   path `nomarchy-menu-rows` no longer leaks in.

New .githooks/pre-commit re-runs the generator and stages docs/SCRIPTS.md
whenever a nomarchy-* script changes. Enable per clone with
`git config core.hooksPath .githooks` (now mentioned in docs/AGENT.md).

Net audit shift after regen: unused? scripts 31→29, missing tokens 30→28,
no false-positive prefix tokens remain.
2026-04-26 08:44:13 +01:00
Bernardo Magri
034da701a3 feat(system): add laptop power preset module
New `nomarchy.system.laptop.{enable,thermald}` options. `enable`
defaults to `formFactor == "laptop"`, so the installer's existing
formFactor write auto-flips the preset on without installer changes.

The module wires TLP (governors + 75/80 charge thresholds),
force-disables power-profiles-daemon (mutually exclusive with TLP),
enables upower and thermald (x86_64), adds the brightnessctl udev
rule so the existing brightness scripts work without root, and sets
a logind lid-switch policy that resolves to suspend-then-hibernate
when `hibernation.enable` is on, plain suspend otherwise.

Closes the "Form-factor → laptop preset auto-enable" Now item and
the "Laptop preset module" Next item from docs/ROADMAP.md in one
change.
2026-04-26 08:31:19 +01:00
Bernardo Magri
7086a6f29c feat(installer): add software-profile multi-select
- Add select_profiles step with gum choose --no-limit
- Implement state persistence and review for selected profiles
- Map profiles to home.packages and system-level toggles (Docker, Steam)
- Update generate_flake_config to emit profile-specific Nix snippets
- Fix duplicate environment.systemPackages in virtualization.nix
- Update ROADMAP.md
2026-04-25 22:44:24 +01:00
Bernardo Magri
1545e63c7d docs: update roadmap and scripts audit status after phase B 2026-04-25 22:40:33 +01:00
Bernardo Magri
f965f0be2c feat(audit): address batch 4 and finalize script audit
- Implement nomarchy-skill, nomarchy-manual, nomarchy-backup, nomarchy-install
- Implement nomarchy-install-docker-dbs (stub)
- Port nomarchy-docs-keybindings and nomarchy-docs-scripts to packaged scripts
- Add installerVm to flake.nix nixosConfigurations, packages, and apps
- Update nomarchy-test-installer to use nix run .#installerVm
- Add docker support to virtualization.nix and options.nix
- Add glow to script dependencies
- Finalize docs/SCRIPTS.md update
2026-04-25 22:39:11 +01:00
Bernardo Magri
fb22e390e8 feat(audit): address batch 3 of missing scripts
- Implement nomarchy-pkg-install, nomarchy-pkg-drop, nomarchy-pkg-aur-add (stub)
- Implement nomarchy-theme, nomarchy-font, nomarchy-wallpaper wrappers
- Update docs/SCRIPTS.md with 'kept' status for new scripts
2026-04-25 22:37:06 +01:00
Bernardo Magri
074dc3576c feat(audit): address batch 2 of missing scripts
- Implement nomarchy-version, nomarchy-debug, nomarchy-reinstall, nomarchy-rollback, nomarchy-upload-log
- Implement nomarchy-refresh-hyprland and nomarchy-refresh-waybar
- Update docs/SCRIPTS.md with 'kept' status for new scripts
2026-04-25 22:36:19 +01:00
Bernardo Magri
0728da4374 feat(audit): address batch 1 of missing scripts and enable fwupd
- Move 18 Hyprland/desktop scripts from features/desktop/scripts/ to packaged directories
- Add nomarchy.hardware.fwupd option (default false) and enable service
- Implement nomarchy-update-firmware wrapper for fwupdmgr
- Add hyprland, swayosd, and fwupd to nomarchy-system-scripts dependencies
- Update docs/SCRIPTS.md with 'kept' status for ported scripts
2026-04-25 22:34:04 +01:00
Bernardo Magri
983ade0f55 fix(theme): wire obsidian sync into theme-set; drop vscode placeholder
Phase B verdict on two unused? entries in the theme-engine scripts.

- nomarchy-theme-set-obsidian: real script that copies the active
  theme's obsidian.css into every Obsidian vault under
  ~/.config/obsidian/obsidian.json. Wires it into nomarchy-theme-set
  next to the btop/opencode hot-reloads. Self-gates twice (no
  obsidian.css → exit 0; no .obsidian dir → continue), so it's a
  no-op for users without Obsidian.

- nomarchy-theme-set-vscode: delete-dead. Its own comment admitted
  it was "mostly a placeholder"; its only action (nomarchy-env-update)
  is already done unconditionally upstream by nomarchy-theme-set.
  The NOMARCHY_TOGGLE_SKIP_VSCODE_THEME env var it gated on is
  exported by features/scripts/default.nix:73 from
  nomarchy.toggles.skipVsCodeTheme, but with this script gone there
  are no consumers; the toggle survives as a public option until a
  follow-up wires it through the VSCode module properly.

SCRIPTS.md regenerated: unused? 34 → 32, kept 165 → 166. nix flake
check clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:18:03 +01:00
Bernardo Magri
d2b508485a fix(theme): hot-reload btop and opencode on theme switch
Phase B verdict on two unused? scripts — both inline comments
claimed they were "used by the Nomarchy theme switching", but the
switcher (themes/engine/scripts/nomarchy-theme-set) only restarted
walker, waybar, and the wallpaper service. So btop and opencode
stayed on the old palette after `nomarchy-theme-set <foo>` until
the user closed and reopened them by hand.

Wires both into nomarchy-theme-set, alongside the existing walker /
waybar restart calls. The check-then-call (`command -v ... &&`)
matches the surrounding style — a missing helper is a no-op, not a
fatal.

SCRIPTS.md regenerated: unused? 36 → 34, kept 163 → 165.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:13:52 +01:00
Bernardo Magri
cc93491232 chore(audit): delete-dead — unused nomarchy-hw-* detection scripts
Phase B verdict on four core/system/scripts/nomarchy-hw-* entries
flagged `unused?` in the Phase A inventory. Wide grep confirmed
the only references were the audit doc itself.

Removed:
  - nomarchy-hw-framework16 (superseded by `nomarchy-hw-match "Laptop 16"`
    in nomarchy-on-boot)
  - nomarchy-hw-surface     (no caller; "Surface" string would route
    through nomarchy-hw-match if needed)
  - nomarchy-hw-intel       (no caller; vendor detection isn't a public
    API — installer/hardware-db.sh handles install-time dispatch and
    nomarchy.hardware.* options handle build-time)
  - nomarchy-hw-intel-ptl   (same — Panther Lake GPU detection isn't
    used anywhere)

Kept: nomarchy-hw-match (the dispatcher), nomarchy-hw-asus-rog
(called by nomarchy-on-boot), nomarchy-hw-vulkan (called by
nomarchy-voxtype-install).

SCRIPTS.md regenerated: unused? 40 → 36; nix flake check clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:11:47 +01:00
Bernardo Magri
aa20399210 docs(audit): generator discovers references in *.md, README, and txt
Scope: small Phase B improvement to the discovery heuristic so the
audit table stops false-flagging documented user-CLI tools as unused.

The generator now grep -r searches *.md, *.txt, *.sample alongside
*.nix / *.conf / *.sh, and explicitly walks README.md. SCRIPTS.md,
ROADMAP.md, and AGENT.md are excluded from the search (they document
the scripts but aren't callers — including them would promote every
script to `kept`).

Status histogram: 158 → 163 kept, 45 → 40 unused?, 75 → 85 missing
(the missing bump comes from grepping aspirational scripts named in
ROADMAP — wait, that doc is excluded — so the new missing rows are
references in MIGRATION/STRUCTURE/creating-themes that name scripts
which don't exist).

Per-script triage of the remaining 40 unused? rows is the next Phase
B batch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:02:45 +01:00
Bernardo Magri
86bc0e570b docs: Pillar 3 Phase A — script & menu audit inventory
bin/utils/nomarchy-docs-scripts walks features/scripts/utils,
core/system/scripts, and themes/engine/scripts; emits a populated
SCRIPTS.md with three tables:

- Scripts (136): location, top callers, status (kept / unused?).
- Missing references: tokens grepped from code with no script file
  (75 rows tagged missing).
- Menu items: every case arm in nomarchy-menu's show_*_menu
  functions, mapped to its target command and tagged.

Status histogram: 158 kept, 75 missing, 45 unused?. Phase B opens
per-batch PRs that refine missing → port-from-omarchy /
delete-dead / stub-with-notify, and unused? → kept / delete-dead.

Roadmap and AGENT.md updated to point at the generator and
explain the Phase B workflow. Now-column row replaced with the
Phase B handoff.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:59:06 +01:00
Bernardo Magri
bf30cd07d8 feat(installer): richer disk picker (vendor, model, serial, type)
Replaces the bare `NAME SIZE` lsblk listing in select_disk with a
six-column table — NAME, SIZE, TYPE, VENDOR, MODEL, SERIAL — aligned
via column -t. TYPE is derived from ROTA + TRAN (NVMe / USB / SSD /
HDD). Empty vendor/model/serial fields render as `--` instead of
collapsing the alignment. Filters loop, ram, zram, sr devices.

Roadmap row moves to Shipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:49:36 +01:00
Bernardo Magri
c9ff6f26f3 docs: add KEYBINDINGS.md generated from Hyprland bindings
bin/utils/nomarchy-docs-keybindings parses every bindd= / bindeld=
line in the core + feature binding files into a six-section Markdown
table (Utilities, Tiling, Tiling v2, Clipboard, Media keys, Apps).
233 bindings rendered. code:NN keycodes and XF86* media keys are
prettified.

README's keybinding table is slimmed to five highlights and now
links the generated doc; the roadmap's Now-column row moves to
Shipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:47:15 +01:00
Bernardo Magri
133ef9ddfc docs: add AGENT.md briefing for AI agents continuing the roadmap
Self-contained handbook so a fresh agent (or future-me) can land
useful work on the first turn: vision, repo layout, guardrails,
how to find work, the per-change workflow, common patterns, and
hard-don't-do rules. Points at ROADMAP.md / SCRIPTS.md as the
durable work queue.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:39:38 +01:00
Bernardo Magri
f09bfbc4e7 docs: relocate MIGRATION.md into docs/
Keeps every long-form doc under docs/ — only README.md remains at the
repo root. Updates the two references (README.md, docs/ROADMAP.md).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:36:24 +01:00
Bernardo Magri
2950dd171e docs: add ROADMAP.md + SCRIPTS.md, retire TODO.md
ROADMAP.md is the durable mid-term plan: vision, guardrails, Now/Next/
Later board, and seven pillars (audit, installer, power/presets,
onboarding/docs, test/CI/release, process). SCRIPTS.md is the
scaffolding for the Pillar 3 script & menu audit — methodology,
generator commands, and a snapshot of currently orphaned callers.

The two open items in TODO.md (software-profile multi-select, richer
disk metadata) move into the roadmap's Now column; the rest of TODO.md
was already shipped, so the file is removed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:35:28 +01:00
Bernardo Magri
6ef28f022b docs: link MIGRATION.md from README
Surface the in-place migration path next to the clean-install wizard
so existing NixOS users discover it without spelunking the repo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:19:53 +01:00
Bernardo Magri
3cb012bcba docs: add OPTIONS.md reference, link from README
Catalogues every nomarchy.{system,hardware,…} and nomarchy.* (home) option
so downstream flake users can see what's available without grepping
options.nix. Linked from the Configuration & Usage section of README.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 21:17:51 +01:00
Bernardo Magri
e438004cec chore: misc tweaks — nm-applet autostart, thunar, SUPER+Q close, monitor preset
- autostart nm-applet --indicator under uwsm-app
- install networkmanagerapplet system-wide
- swap Nautilus for Thunar in file-manager bindings
- close-window bound to SUPER+Q (was SUPER+W)
- switch the active monitor preset from retina/2x to 1x 1080p/1440p
- summer-night waybar: drop custom/battery + backlight from modules-right

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 20:28:19 +01:00
Bernardo Magri
a7e7fa9562 feat: keymap/locale + form factor in installer; nm-applet visible by default
- Installer prompts for keyboard layout (with optional variant) and locale
  via curated short list + Other… fallback into the full localectl list;
  applies to the live session immediately (loadkeys + hyprctl) so the
  rest of the install types correctly. Generated system.nix emits
  console.keyMap, i18n.defaultLocale, and services.xserver.xkb.{layout,
  variant}.
- New nomarchy.{system,}.formFactor enum (laptop|desktop, default laptop).
  Installer auto-detects via /sys/class/power_supply/BAT* and lets the
  user flip the answer. Waybar drops the battery widget on desktop;
  battery-monitor service is gated on the same option.
- Lift waybar tray out of the collapsed group/tray-expander in the default
  theme so nm-applet's icon is visible without expanding the drawer.
- Live ISOs (TTY + graphical) get baseline mkDefault keyMap/locale so the
  installer's runtime override always wins.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 20:26:55 +01:00
Bernardo Magri
7fd0f78d7c minors 2026-04-25 16:01:55 +01:00
Bernardo Magri
6203413425 chore: drop makima/Typora/xournalpp; gate fcitx5/voxtype/opencode behind options
Tier A removals — small, half-wired modules nobody had asked for:

- makima (Copilot-key remapper): drop core/system/makima.nix, the
  features/apps/makima/ keyboard.toml, the nomarchy-restart-makima script,
  the `nomarchy.system.features.makima` option, the state-file binding,
  the import in core/system/default.nix, and the "Key Remapping" entry
  in nomarchy-menu. ~50 LoC + a service nobody asked for.
- Typora theme dir (core/home/config/Typora/) — Typora is a paid tool
  Nomarchy doesn't even ship; the SUPER+SHIFT+W keybinding pointed at a
  binary that wasn't on PATH.
- xournalpp settings (core/home/config/xournalpp/) — referenced
  /usr/share paths that don't exist on NixOS.
- core/home/config/environment.d/fcitx.conf — manual env vars are
  redundant once fcitx5 routes through NixOS's i18n.inputMethod.

Optionalization — three half-wired features now sit behind explicit
toggles, all default off (except keyring which keeps its existing
default-on):

- nomarchy.system.inputMethod.enable: new core/system/input-method.nix
  uses NixOS's i18n.inputMethod with fcitx5 + mozc/chinese/table addons.
  Drops the Hyprland exec-once line — i18n.inputMethod handles autostart.
- nomarchy.system.voxtype.enable: marker option for users who install
  voxtype out-of-band (it's not in nixpkgs). Today it just documents
  intent; the existing keybinding + waybar widget no-op gracefully.
- nomarchy.apps.opencode.enable: gates the existing
  features/apps/opencode/default.nix xdg.configFile so the opencode
  config only deploys when the user opts in.

Installer:
- system.nix and home.nix templates now surface the new toggles in their
  "Optional Nomarchy modules" comment blocks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 14:56:28 +01:00
Bernardo Magri
4ddc91b930 feat: Tier 1 system features — snapper, hibernate, containers, libvirt, keyring
Five opt-in modules lifted from bernardo/nixos and adapted to Nomarchy's
nomarchy.system.* option namespace. All default off (except keyring which
defaults on); evaluation of the existing VM/ISO is unchanged when the
toggles are unset.

- core/system/snapper.nix: BTRFS timeline snapshots (5h/7d), nixos-rebuild-snap
  wrapper that pre-snaps before each switch using the running hostname.
  Auto-skips when / isn't BTRFS so impermanence/non-BTRFS hosts are safe.
- core/system/hibernate.nix: suspend-then-hibernate on lid/idle/power-key
  with configurable idleMinutes (default 30). Description warns swap is
  required.
- core/system/containers.nix: rootless Podman with dockerCompat + dns +
  podman-compose, podman-tui, dive. Better default than the docker daemon
  for a desktop distro.
- core/system/virtualization.nix: extends the existing uwsm/Hyprland file
  with a libvirt + virt-manager + OVMF branch behind
  nomarchy.system.virtualization.libvirt.enable.
- core/system/pam.nix: GNOME Keyring auto-unlock at SDDM/login/hyprlock
  plus gcr-ssh-agent so SSH keys flow through the keyring instead of a
  separate ssh-agent. Default on.
- core/system/options.nix: declares the five new options.
- core/system/default.nix: imports the four new files.
- installer/install.sh: surfaces all five toggles as commented one-liners
  in the "Optional Nomarchy modules" section of the generated system.nix.
  Verified via the existing dry-run / generator smoke test.

Verified each toggle lights up the right NixOS option (services.snapper,
logind IdleAction, virtualisation.podman/libvirtd, pam.sddm.enableGnomeKeyring)
via nix eval against extendModules. VM and live-ISO toplevels still build.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 11:18:15 +01:00
Bernardo Magri
220fc7f699 fix: point downstream flakes at git.bemagri.xyz, not github
Upstream Nomarchy is hosted on the self-hosted Gitea at
git.bemagri.xyz/bernardo/Nomarchy.git, not github.com/bemagri/nomarchy.

- installer/install.sh: generated `nomarchy.url` now uses
  `git+https://git.bemagri.xyz/bernardo/Nomarchy.git` (with `?rev=<sha>`
  for the pinned form).
- MIGRATION.md: matches; the `hardware_detect` clone snippet now points
  at the same URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 10:46:59 +01:00
Bernardo Magri
f0bd25f902 fix: stop the "nix-colors / non-existent input nixpkgs" warning
nix-colors is a pure data flake (base16 schemes) — it declares no inputs,
so `inputs.nixpkgs.follows = "nixpkgs"` against it produces a warning on
every evaluation. Drop the override and just `url`-import it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 10:45:31 +01:00
Bernardo Magri
955269f9a2 docs: add MIGRATION.md for in-place adoption from existing NixOS
Documents the layered-adoption path: add nomarchy as a flake input, build
a single pkgs with nomarchy.overlays.default, wire nomarchy.nixosModules
into both nixosSystem and a standalone homeManagerConfiguration, and
rebuild. Preserves the user's hardware-configuration, hostname, and
account; no reformat.

Calls out the conflicts an existing config will hit (DM, Hyprland, audio,
NetworkManager, user groups, /etc/os-release rebrand, autoLogin) with
explicit mkForce/drop-this-line guidance. Points at the live-ISO clean
install as the alternative for users who'd rather start fresh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 10:40:31 +01:00
Bernardo Magri
bb435a47bb fix(live-iso): stop initrd GPU panic + clear install pointer on login
- boot.initrd.kernelModules force-loaded amdgpu+radeon+nouveau+i915 in the
  live ISO, panicking on hosts where more than one driver matched the GPU.
  Move them to availableKernelModules so udev only loads what fits.
- Override services.getty.helpLine with a Nomarchy welcome that points at
  `sudo /etc/install.sh` (+ --dry-run / --resume variants). Replaces the
  upstream "NixOS" message.
- Ship installer/hardware-db.sh as /etc/hardware-db.sh so the installer's
  auto-detection works when invoked from the live ISO.
- Add live-ISO-only Hyprland exec-once: notify-send + an alacritty
  "Nomarchy Installer" terminal that prints the install commands. The
  graphical autologin user now sees a pointer instead of guessing how to
  start the install.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 10:24:49 +01:00
Bernardo Magri
e66537523a feat(installer): UX polish — dry-run, resume, UEFI gate, pre-flight, zram
Adds command-line flags and safety rails on top of the existing install.sh.

CLI:
- `--dry-run` generates the flake into /tmp/nomarchy-dryrun.* and parse-checks
  every produced file without touching the disk. Skips LUKS / user password
  prompts and the destructive confirmation; sets safe stub values.
- `--resume` reloads non-secret answers from /tmp/nomarchy-install.state.sh
  (saved via `declare -p` after each step) and skips already-answered prompts.
  Passwords are NEVER persisted — the user re-enters them.
- `--help` documents the flags.

Safety:
- Bail early in check_environment if /sys/firmware/efi is absent. The disko
  config assumes UEFI + ESP; on a BIOS-booted host we'd partially install
  before failing.
- After nixos-install, run `nixos-rebuild dry-build --flake /etc/nixos#$HOSTNAME`
  inside `nixos-enter` to surface evaluation errors while the live ISO is
  still around to fix them.
- ENABLE_IMPERMANENCE now defaults to "" so the resume path can distinguish
  "not yet asked" from a deliberate "false" answer.

Generated config:
- system.nix gets `zramSwap.enable = true;` — near-free memory headroom on
  small machines, harmless on big ones (kernel only uses it under pressure).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 10:18:41 +01:00
283 changed files with 6622 additions and 2447 deletions

View File

@@ -0,0 +1,102 @@
# Nomarchy CI — eval + lint.
#
# Catches the regressions that hurt today:
# 1. Flake stops evaluating (broken option ref, missing import, etc.).
# 2. A `nomarchy-*` shell script has a syntax error or a shellcheck
# error-severity issue.
# 3. `docs/SCRIPTS.md` drifts from the repo state because somebody
# added / removed / renamed a script and didn't run the generator
# (the pre-commit hook handles this, but only when enabled per-clone).
#
# Doesn't build ISOs — that needs a binary cache. Add a separate job
# once Cachix/Attic is in place.
name: Check
on:
push:
branches: [main]
pull_request:
jobs:
eval-and-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
# Match the runner's effective channel. Nomarchy itself tracks
# nixos-25.11 via flake.nix; the installer-action default is fine.
extra-conf: |
experimental-features = nix-command flakes
- name: nix flake check --no-build
run: nix flake check --no-build
- name: Lint nomarchy-* scripts (bash -n + shellcheck)
run: |
# Mirror what .githooks/pre-commit runs locally, but across the
# whole tree instead of just changed files. Pre-commit gates
# individual commits; CI gates branches (including --no-verify
# bypasses).
set -e
fail=0
while IFS= read -r script; do
[[ -f "$script" ]] || continue
# Python helpers ship under the same nomarchy- prefix
# (e.g. nomarchy-haptic-touchpad). Skip non-bash.
head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue
if ! bash -n "$script"; then
echo "::error file=$script::bash syntax error"
fail=1
fi
if ! nix shell nixpkgs#shellcheck --command shellcheck \
--severity=error --shell=bash "$script"; then
echo "::error file=$script::shellcheck error-severity issue"
fail=1
fi
done < <(find features/scripts/utils core/system/scripts \
themes/engine/scripts \
-maxdepth 1 -type f -name 'nomarchy-*')
exit "$fail"
- name: docs/SCRIPTS.md is up to date
run: |
# Regenerate to a temp file and compare. If different, the
# contributor forgot to run the generator (or skipped the
# pre-commit hook). Fail loudly and tell them the fix.
./bin/utils/nomarchy-docs-scripts --out /tmp/SCRIPTS.regen.md
if ! diff -q docs/SCRIPTS.md /tmp/SCRIPTS.regen.md >/dev/null; then
echo "::error::docs/SCRIPTS.md is stale."
echo "Run: ./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md"
echo "Then commit the regenerated file."
echo "--- diff ---"
diff -u docs/SCRIPTS.md /tmp/SCRIPTS.regen.md || true
exit 1
fi
- name: installer/hardware-db.sh references real nixos-hardware modules
run: |
# Every 4th-pipe-field in HARDWARE_DB is a nixos-hardware module
# name. Half the DB used to point at modules that don't exist
# (e.g. microsoft-surface-pro-8 — there's only -pro-intel and
# -pro-9), which made the install fail at eval time with
# cryptic "attribute not found" errors on real laptops. This
# step catches that regression class.
awk -F'|' '/^ "/ { gsub(/"/,"",$4); gsub(/^[[:space:]]+|[[:space:]]+$/,"",$4); if ($4) print $4 }' \
installer/hardware-db.sh | sort -u > /tmp/db-refs.txt
nix eval --impure --json --expr '
let
nh = (builtins.getFlake (toString ./.)).inputs.nixos-hardware.nixosModules;
in builtins.attrNames nh' \
| nix shell nixpkgs#jq --command jq -r '.[]' | sort -u > /tmp/db-real.txt
missing=$(comm -23 /tmp/db-refs.txt /tmp/db-real.txt)
if [[ -n "$missing" ]]; then
echo "::error::hardware-db.sh references nixos-hardware modules that don't exist:"
printf ' - %s\n' $missing
echo "Either fix the name (check the actual attr in nixos-hardware) or drop the row."
exit 1
fi

54
.githooks/pre-commit Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Nomarchy pre-commit hook.
#
# Enable per-clone with:
# git config core.hooksPath .githooks
#
# Two responsibilities:
# 1. Lint changed nomarchy-* scripts (bash -n + shellcheck if available)
# so syntax errors and unquoted-var bugs don't ship.
# 2. Regenerate docs/SCRIPTS.md when any nomarchy-* script under the three
# script directories is added, modified, or deleted in this commit, and
# stage the refreshed file so it lands with the change.
set -e
repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"
script_dirs_re='^(features/scripts/utils|core/system/scripts|themes/engine/scripts)/nomarchy-'
# 1. Lint changed scripts. bash -n catches syntax errors (always fatal).
# shellcheck catches unquoted-var, use-before-define, missing-shebang, etc.
# We only fail on severity=error so the long tail of pre-existing warnings
# (info / style / warning) doesn't block commits — those can be cleaned up
# incrementally without a flag day.
changed_scripts=$(git diff --cached --name-only --diff-filter=ACMR \
| grep -E "$script_dirs_re" || true)
if [[ -n "$changed_scripts" ]]; then
while IFS= read -r script; do
[[ -f "$script" ]] || continue
# Only lint scripts with a bash shebang. nomarchy-* is a name
# convention, not a language guarantee — at least one Python helper
# ships under the same prefix (nomarchy-haptic-touchpad).
head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue
if ! bash -n "$script"; then
echo "pre-commit: bash syntax error in $script — aborting commit." >&2
exit 1
fi
if command -v shellcheck >/dev/null 2>&1; then
if ! shellcheck --severity=error --shell=bash "$script"; then
echo "pre-commit: shellcheck found error-level issues in $script — aborting commit." >&2
echo "pre-commit: fix the reported issues, or rerun with --no-verify after a deliberate decision to ship." >&2
exit 1
fi
fi
done <<< "$changed_scripts"
fi
# 2. Regenerate the script audit doc.
if git diff --cached --name-only --diff-filter=ACMRD | grep -qE "$script_dirs_re"; then
echo "pre-commit: regenerating docs/SCRIPTS.md (script change detected)…"
./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md
git add docs/SCRIPTS.md
fi

View File

@@ -1,6 +1,6 @@
# 👑 Nomarchy # 👑 Nomarchy
**Nomarchy** is a professional-grade NixOS distribution that replicates the Omarchy Wayland workflow with a strictly declarative, flake-based architecture. It provides a highly polished, "it just works" experience for power users who want a beautiful Hyprland environment without sacrificing the reliability of NixOS. **Nomarchy** is a professional-grade NixOS distribution that ships a highly curated Hyprland desktop on a strictly declarative, flake-based foundation. It provides a highly polished, "it just works" experience for power users who want a beautiful Wayland environment without sacrificing the reliability of NixOS.
## ✨ Key Features ## ✨ Key Features
@@ -40,7 +40,8 @@ This builds a full graphical VM of the installer environment. Once inside, click
### 2. Build the Installer ISO ### 2. Build the Installer ISO
To install on physical hardware, generate your own bootable image: To install on physical hardware, generate your own bootable image:
```bash ```bash
nix build .#nixosConfigurations.installerIso.config.system.build.isoImage ./features/scripts/utils/nomarchy-build-iso # Minimal TTY installer
./features/scripts/utils/nomarchy-build-live-iso # Graphical try-before-install
``` ```
The ISO will be located at `./result/iso/nixos-*.iso`. Flash it to a USB drive and boot. The ISO will be located at `./result/iso/nixos-*.iso`. Flash it to a USB drive and boot.
@@ -55,6 +56,9 @@ The wizard will guide you through:
- **Storage:** Choice between Standard Ext4 or Encrypted BTRFS with optional **Impermanence**. - **Storage:** Choice between Standard Ext4 or Encrypted BTRFS with optional **Impermanence**.
- **Localization:** Searchable timezones and keyboard layout selection. - **Localization:** Searchable timezones and keyboard layout selection.
### Already on NixOS?
Layer Nomarchy onto an existing 25.11 install without reformatting — see the [Migration Guide](docs/MIGRATION.md).
--- ---
## 🛠️ Configuration & Usage ## 🛠️ Configuration & Usage
@@ -80,6 +84,8 @@ Add user-level packages, aliases, and dotfiles here.
nomarchy.home.terminal = "kitty"; 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).
### Applying Changes ### 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: 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:
@@ -92,12 +98,12 @@ env-update # Reloads your Home Manager environment (Runs: home-manager switch -
## 🚀 Commands & Keybindings ## 🚀 Commands & Keybindings
The full list lives in [`docs/KEYBINDINGS.md`](docs/KEYBINDINGS.md) (auto-generated from the Hyprland configs). A few highlights:
| Keybinding | Action | | Keybinding | Action |
| :--- | :--- | | :--- | :--- |
| `Super + Space` | **App Launcher** (Walker) | | `Super + Space` | **App Launcher** (Walker) |
| `Super + Shift + Space` | **Nomarchy Menu** (Walker) | | `Super + Shift + Space` | **Nomarchy Menu** (Walker) |
| `Super + Ctrl + Space` | **Background Selector** (Walker) |
| `Super + Shift + Ctrl + Space` | **Theme Selector** (Walker) |
| `Super + Alt + Space` | **Toggle Top Bar** (Waybar) | | `Super + Alt + Space` | **Toggle Top Bar** (Waybar) |
| `Super + Return` | Open Terminal | | `Super + Return` | Open Terminal |
| `Super + Q` | Close Window | | `Super + Q` | Close Window |
@@ -109,4 +115,4 @@ Nomarchy includes dozens of productivity scripts available in your PATH. Some hi
- `nomarchy-menu`: The central hub for all utilities and pickers. - `nomarchy-menu`: The central hub for all utilities and pickers.
--- ---
*Built with ❤️ using NixOS, Hyprland, Stylix, and the spirit of Omarchy.* *Built with ❤️ using NixOS, Hyprland, Stylix, and the Nomarchy Community.*

30
TODO.md
View File

@@ -1,30 +0,0 @@
# Nomarchy Distribution - TODO List
## 1. Professional Installer [IN PROGRESS]
- [x] Refactor with `gum` for stylized UI.
- [x] Add clear section headers and environment checks.
- [ ] Implement software profile selection.
- [x] Add "Pre-flight Check" summary screen showing all selected options (disk, user, profiles) before proceeding.
- [x] Implement post-install "Success" dashboard with next steps.
- [ ] Improve disk selection to show more metadata (vendor, model, serial).
## 2. Nomarchy Plymouth Theme [DONE]
- [x] Create custom theme assets in `assets/plymouth/`.
- [x] Center official logo on splash screen.
- [x] Declarative integration in `modules/system/plymouth.nix`.
- [x] Implement full LUKS password entry support (password prompt, bullet indicators).
- [x] Add smooth fade-in/fade-out animations for the logo.
- [x] Add system message display (e.g., "Rebooting...", "Checking Disk...").
## 3. System Branding (Fastfetch & Terminal) [DONE]
- [x] Restore and optimize official ASCII logo (`logo.txt`).
- [x] Update `config/fastfetch/config.jsonc` to use new logo and declarative stats.
- [x] Update `nomarchy-show-logo` to use centralized branding path.
- [x] Declaratively map all branding assets via Home Manager.
## 4. Repo Hygiene & Consistency [DONE]
- [x] Remove legacy Arch Linux imperative scripts.
- [x] Move all dynamic state to unified `state.json`.
- [x] Implement professional state migration via Home Manager activation.
- [x] Declaratively link all `config/` directories with `recursive = true`.
- [x] Transition to Remote-First Upstream model.

View File

@@ -0,0 +1,109 @@
#!/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-v2.conf|Tiling"
"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

276
bin/utils/nomarchy-docs-scripts Executable file
View File

@@ -0,0 +1,276 @@
#!/usr/bin/env bash
# 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 first `grep -vE` drops lines where `nomarchy-*` is a derivation /
# tmp file / sudoers basename / systemd unit / flake output / docker
# container identifier rather than a shell invocation.
# The second `grep -vE` is a token-level safety net for prefix-only
# tokens left over from wildcards/expansions (e.g. `nomarchy-pkg-*`).
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-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)$' \
| sort -u)
# The second denylist 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`
# (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

16
core/branding/about.txt Normal file
View File

@@ -0,0 +1,16 @@
# About Nomarchy
Nomarchy is a highly curated, NixOS-based distribution designed for power users.
It features a customized Hyprland desktop environment with a declarative
theming engine and a suite of integrated utility scripts.
Built on a foundation of:
- NixOS (Linux)
- Hyprland (Window Manager)
- Waybar (Status Bar)
- Walker (Application Launcher & Menu)
- Stylix (Theming Engine)
Version: 2026.05.04
Docs: https://github.com/nomarchy/nomarchy/docs
Manual: nomarchy-manual (Command)

View File

@@ -0,0 +1,11 @@
# Nomarchy Screensaver Configuration
Nomarchy uses hyprlock for locking and hypridle for idle management.
To configure the screensaver/lock screen visuals, edit:
~/.config/hypr/hyprlock.conf
To configure idle timeouts and actions, edit:
~/.config/hypr/hypridle.conf
You can also toggle the screensaver/idle management via the Nomarchy Menu:
Trigger > Toggle > Idle Lock

View File

@@ -1,94 +0,0 @@
{ config, lib, ... }:
# Behavior Configuration Module
#
# This module deploys non-visual configuration files (keybindings, input settings,
# window rules, etc.) with lib.mkDefault, allowing downstream users to override.
#
# Visual/theme configs are handled separately by theme-loader.nix and stylix.nix.
#
# Behavior configs include:
# - Keybindings (bindings, media keys, clipboard)
# - Input settings (keyboard, mouse, touchpad)
# - Window rules and layouts
# - Autostart applications
# - Environment variables
let
configDir = ./config;
overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides";
# Behavior config categories with their source paths
behaviorConfigs = {
# Hyprland behavior (non-visual)
"nomarchy/default/hypr/bindings.conf" = "hypr/bindings.conf";
"nomarchy/default/hypr/bindings/media.conf" = "hypr/bindings/media.conf";
"nomarchy/default/hypr/bindings/clipboard.conf" = "hypr/bindings/clipboard.conf";
"nomarchy/default/hypr/bindings/tiling-v2.conf" = "hypr/bindings/tiling-v2.conf";
"nomarchy/default/hypr/bindings/utilities.conf" = "hypr/bindings/utilities.conf";
"nomarchy/default/hypr/input.conf" = "hypr/input.conf";
"nomarchy/default/hypr/windows.conf" = "hypr/windows.conf";
"nomarchy/default/hypr/autostart.conf" = "hypr/autostart.conf";
"nomarchy/default/hypr/envs.conf" = "hypr/envs.conf";
"nomarchy/default/hypr/looknfeel.conf" = "hypr/looknfeel.conf";
# App-specific window rules (behavior, not visual)
"nomarchy/default/hypr/apps.conf" = "hypr/apps.conf";
"nomarchy/default/hypr/apps/qemu.conf" = "hypr/apps/qemu.conf";
"nomarchy/default/hypr/apps/steam.conf" = "hypr/apps/steam.conf";
"nomarchy/default/hypr/apps/terminals.conf" = "hypr/apps/terminals.conf";
"nomarchy/default/hypr/apps/walker.conf" = "hypr/apps/walker.conf";
"nomarchy/default/hypr/apps/browser.conf" = "hypr/apps/browser.conf";
"nomarchy/default/hypr/apps/1password.conf" = "hypr/apps/1password.conf";
"nomarchy/default/hypr/apps/bitwarden.conf" = "hypr/apps/bitwarden.conf";
"nomarchy/default/hypr/apps/pip.conf" = "hypr/apps/pip.conf";
"nomarchy/default/hypr/apps/system.conf" = "hypr/apps/system.conf";
"nomarchy/default/hypr/apps/localsend.conf" = "hypr/apps/localsend.conf";
"nomarchy/default/hypr/apps/telegram.conf" = "hypr/apps/telegram.conf";
"nomarchy/default/hypr/apps/geforce.conf" = "hypr/apps/geforce.conf";
"nomarchy/default/hypr/apps/moonlight.conf" = "hypr/apps/moonlight.conf";
"nomarchy/default/hypr/apps/retroarch.conf" = "hypr/apps/retroarch.conf";
"nomarchy/default/hypr/apps/webcam-overlay.conf" = "hypr/apps/webcam-overlay.conf";
"nomarchy/default/hypr/apps/davinci-resolve.conf" = "hypr/apps/davinci-resolve.conf";
"nomarchy/default/hypr/apps/hyprshot.conf" = "hypr/apps/hyprshot.conf";
};
in
{
options.nomarchy.behavior = {
hyprland = {
bindings = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default Hyprland keybindings.";
};
input = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default input settings.";
};
windowRules = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default window rules.";
};
autostart = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default autostart configuration.";
};
};
};
config = {
# Note: The actual config deployment is handled by configs.nix
# This module provides the options and documentation for behavior configs
# The separation allows users to selectively disable behavior categories
# Ensure behavior config directories exist in overrides
home.activation.createBehaviorOverrideDirs = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p "${overridesDir}/hypr/bindings"
mkdir -p "${overridesDir}/hypr/apps"
'';
};
}

View File

@@ -1,309 +0,0 @@
:root {
--accent-color: #aeb1b5; /* #'s before H1-3 */
--background-color: white;
--border-color: #ddd;
--code-bg-color: #f8f8f8;
--font-color: #42464c;
--header-color: #222324;
--link-color: #2077b2;
--control-text-color: #667176;
--side-bar-bg-color: #fafafa;
--body-font: "iA Writer Mono S";
--border-radius: 2px;
--document-horizontal-margin: 80px;
--document-padding-x: 10ch;
--font-size: 20px;
--h1-fontsize: 20px; /* 1,5 rem = 24px */
--h2-fontsize: 20px; /* 1,375 rem = 22px */
--h3-fontsize: 20px; /* 1,25 rem = 20px */
--h4-fontsize: 20px; /* 1,125 rem = 18px */
--line-height: 1.8;
--main-content-margin: 0 auto;
--main-content-max-width: 85ch;
--monospace-font-size: 20px;
--monospace-font: "iA Writer Mono S";
--monospace-line-height: 1.6;
--monospace: var(--monospace-font); /* Fixes source code mode */
--nav-width: 200px;
--vertical-padding: 40px;
}
/* Narrow layout styles */
@media (max-width: 640px) {
:root {
--document-padding-x: 4ch;
}
}
html {
font-size: var(--font-size);
background-color: var(--background-color);
}
html,
body {
color: var(--font-color);
font-family: var(--body-font);
line-height: var(--line-height);
}
#write {
/* size of writing area: */
padding-left: var(--document-padding-x);
padding-right: var(--document-padding-x);
max-width: var(--main-content-max-width);
box-sizing: border-box;
}
/**
* ---------------------
* Block Elements
*/
/* yaml */
pre.md-meta-block {
background-color: var(--background-color);
padding-bottom: .5rem;
color: var(--accent-color);
border-bottom: 2px solid var(--border-color);
font-family: var(--monospace-font);
}
/* headings */
h1, h2, h3 {
font-weight: bold;
color: var(--header-color);
}
h1 {
margin-top: 1rem;
margin-bottom: 1rem;
font-size: var(--h1-fontsize);
}
h1:not(:first-child) {
margin-top: 1rem;
}
h2 {
margin-top: 1rem;
margin-bottom: 0rem;
font-size: var(--h2-fontsize);
}
h3 {
margin-top: 1rem;
margin-bottom: 0rem;
font-size: var(--h3-fontsize);
}
h4, h5, h6 {
color: var(--header-color);
margin-top: 1rem;
margin-bottom: 0rem;
font-size: 1rem;
}
h4 {
font-size: var(--h4-fontsize);
}
h6 {
color: var(--control-text-color);
}
h1::before,
h2::before,
h3::before {
font-weight: 400;
text-align: right;
width: 5ch;
padding-right: 1ch;
margin-left: -5ch;
color: var(--accent-color);
display: inline-block;
}
h1::before {
content: '#';
}
h2::before {
content: '##';
}
h3::before {
content: '###';
}
h1 tt,
h1 code {
font-size: inherit;
}
h2 tt,
h2 code {
font-size: inherit;
}
h3 tt,
h3 code {
font-size: inherit;
}
h4 tt,
h4 code {
font-size: inherit;
}
h5 tt,
h5 code {
font-size: inherit;
}
h6 tt,
h6 code {
font-size: inherit;
}
/* table */
table {
margin-top: 1rem;
margin-bottom: 1rem;
}
table tr:nth-child(2n),
thead {
background-color: var(--side-bar-bg-color);
}
td,
th {
border-style: solid;
border-color: var(--border-color);
border-width: 1px;
padding: .35rem .7rem
}
li p {
margin: 0;
}
.task-list {
padding-left: 0;
}
.task-list-item {
padding-left: 1.5em;
margin-bottom: 0rem;
}
#write input[type='checkbox'] {
margin-top: 5px;
}
blockquote {
margin: 1rem 0;
padding-left: 2ch;
margin-left: .5ch;
position: relative;
overflow: hidden;
border-left: 2px solid var(--border-color);
color: var(--accent-color);
}
/* horizontal line */
hr {
border: none;
border-bottom: 2px solid var(--border-color);
margin-top: 1.4rem;
margin-bottom: 1.4rem;
}
p {
margin: 2rem 0;
}
/**
* Code Fences
* see http:/*support.typora.io/Code-Block-Styles
*/
.cm-s-inner .CodeMirror-gutters {
background: var(--code-bg-color);
}
.code-tooltip {
box-shadow: none;
border-radius: var(--border-radius);
background-color: var(--code-bg-color);
border-color: var(--border-color);
border-style: solid;
border-width: 1px;
}
.code-tooltip input {
outline: none;
width: 20ch;
}
.md-fences .code-tooltip {
bottom: -1.8rem;
padding: none;
}
.md-fences.md-focus .cm-s-inner {
/* Remove bottom right border radius when tooltip is shown */
border-bottom-right-radius: 0;
}
/* code tooltip autocomplete list */
.autoComplt-hint {
background-color: transparent !important;
margin: 0 !important;
padding: 0.125rem 0.375rem !important;
color: var(--text-color) !important;
line-height: var(--line-height) !important;
height: 1.4rem !important;
}
.autoComplt-hint-selected {
background-color: var(--link-color) !important;
color: var(--background-color) !important;
}
/* basic styles */
.md-fences,
code,
tt {
border: 1px solid var(--border-color);
background-color: var(--code-bg-color);
font-family: var(--monospace-font);
font-size: var(--monospace-font-size);
border-radius: .25rem;
padding: 0 .125rem;
line-height: var(--monospace-line-height);
}
.md-fences {
margin-bottom: 18px;
margin-top: 15px;
padding: 0.2em 1em;
padding-top: 8px;
padding-bottom: 6px;
}
a {
color: var(--link-color);
}
/**
* Control UI (optional)
*/
.outline-item:hover {
color: var(--header-color);
}
#write div.md-toc-tooltip {
background-color: var(--background-color);
}

View File

@@ -1,95 +0,0 @@
@import 'night/codeblock.dark.css';
@import 'night/mermaid.dark.css';
@import 'night/sourcemode.dark.css';
@import 'ia_typora.css';
:root {
--accented-background-color: #1e1e1e;
--light-header-color: #dbdbdb; /* H1-H3 */
--select-text-bg-color: #186a9a;
--accent-color: #4f525a;
--background-color: #101010;
--font-color: #bbbcbc;
--header-color: #bebebe; /* H4-H6 */
--border-color: #232629;
--link-color: #5584aa;
--code-bg-color: #1c1a1a;
--hover-bg-color: #050505;
--control-text-color: var(--font-color);
--side-bar-bg-color: var(--accented-background-color);
--control-text-hover-color: var(--header-color);
--item-hover-text-color: var(--header-color);
--item-hover-bg-color: var(--hover-bg-color);
--bg-color: var(--background-color);
--text-color: var(--font-color);
--meta-content-color: var(--accent-color);
--md-char-color: var(--accent-color);
--window-border: 1px solid rgba(0,0,0,.07);
--active-file-bg-color: var(--hover-bg-color);
}
.outline-item:hover {
background-color: var(--hover-bg-color);
}
#write {
cursor: -webkit-image-set(url("night/cursor.png") 1x, url("night/cursor@2x.png") 2x) 8 8, auto;
}
h1,
h2,
h3 {
color: var(--light-header-color);
}
mark {
background: #b8b80a
}
table tr:nth-child(2n),
thead {
background-color: #141313;
}
sup.md-footnote {
background-color: var(--code-bg-color);
color: var(--font-color);
}
/* right click menu */
.context-menu {
background-color: #141313;
}
.context-menu.dropdown-menu .divider {
background-color: #232629;
}
/* focus mode */
.on-focus-mode .md-end-block:not(.md-focus):not(.md-focus-container) * {
color: #4f525a !important;
}
.on-focus-mode .md-end-block:not(.md-focus) img,
.on-focus-mode .md-task-list-item:not(.md-focus-container)>input {
opacity: #4f525a !important;
}
.on-focus-mode li[cid]:not(.md-focus-container){
color: #4f525a;
}
.on-focus-mode .md-fences.md-focus .CodeMirror-code>*:not(.CodeMirror-activeline) *,
.on-focus-mode .CodeMirror.cm-s-inner:not(.CodeMirror-focused) * {
color: #4f525a !important;
}
.on-focus-mode .md-focus,
.on-focus-mode .md-focus-container {
color: var(--light-header-color);
}
.on-focus-mode #typora-source .CodeMirror-code>*:not(.CodeMirror-activeline) * {
color: #4f525a !important;
}

View File

@@ -1,4 +0,0 @@
INPUT_METHOD=fcitx
QT_IM_MODULE=fcitx
XMODIFIERS=@im=fcitx
SDL_IM_MODULE=fcitx

View File

@@ -8,7 +8,7 @@ description: >
monitors, gaps, borders, blur, opacity, waybar, walker, terminal config, themes, monitors, gaps, borders, blur, opacity, waybar, walker, terminal config, themes,
wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace
settings, display config, and user-facing nomarchy commands. Excludes Nomarchy settings, display config, and user-facing nomarchy commands. Excludes Nomarchy
source development in ~/.local/share/nomarchy/ and nomarchy-dev-* workflows. source development in ~/.local/share/nomarchy/ and repo-internal workflows.
--- ---
# Nomarchy Skill # Nomarchy Skill
@@ -34,7 +34,7 @@ It is not for contributing to Nomarchy source code.
**If you're about to edit a config file in ~/.config/ on this system, STOP and use this skill first.** **If you're about to edit a config file in ~/.config/ on this system, STOP and use this skill first.**
**Do NOT use this skill for Nomarchy development tasks** (editing files in `~/.local/share/nomarchy/`, creating migrations, or running `nomarchy-dev-*` workflows). **Do NOT use this skill for Nomarchy development tasks** editing files in `~/.local/share/nomarchy/` or modifying repo internals.
## Critical Safety Rules ## Critical Safety Rules
@@ -244,6 +244,9 @@ nomarchy-theme-set <name> # Apply theme (use "Tokyo Night" not "tokyo-nig
nomarchy-theme-next # Cycle to next theme nomarchy-theme-next # Cycle to next theme
nomarchy-theme-bg-next # Cycle wallpaper nomarchy-theme-bg-next # Cycle wallpaper
nomarchy-theme-install <url> # Install from git repo nomarchy-theme-install <url> # Install from git repo
nomarchy-theme-remove <name> # Remove an installed extra theme
nomarchy-theme-refresh # Re-apply current theme from templates
nomarchy-theme-bg-install # Open backgrounds dir to drop in custom images
``` ```
### Keybindings ### Keybindings
@@ -311,10 +314,48 @@ nomarchy-debug --no-sudo --print # Debug info (ALWAYS use these flags)
nomarchy-lock-screen # Lock screen nomarchy-lock-screen # Lock screen
nomarchy-system-shutdown # Shutdown nomarchy-system-shutdown # Shutdown
nomarchy-system-reboot # Reboot nomarchy-system-reboot # Reboot
nomarchy-sudo-passwordless-toggle # Toggle 15-min passwordless sudo
nomarchy-sudo-reset # Clear sudo lockout / faillock
nomarchy-restart-trackpad # Reload intel_quicki2c (fixes dead THC trackpad)
``` ```
**IMPORTANT:** Always run `nomarchy-debug` with `--no-sudo --print` flags to avoid interactive sudo prompts that will hang the terminal. **IMPORTANT:** Always run `nomarchy-debug` with `--no-sudo --print` flags to avoid interactive sudo prompts that will hang the terminal.
### Custom App Launchers
```bash
nomarchy-webapp-install # Add a web app launcher (interactive)
nomarchy-webapp-remove [name...] # Remove web apps (interactive if no name)
nomarchy-webapp-remove-all # Bulk-remove every web app
nomarchy-tui-install # Add a TUI launcher for a terminal program
nomarchy-tui-remove [name...] # Remove TUI launchers
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
nomarchy-windows-vm install # Provision a Windows VM via docker-compose
nomarchy-windows-vm launch # Connect to the VM (auto-stop on disconnect)
nomarchy-windows-vm launch -k # Connect, keep VM running after disconnect
nomarchy-windows-vm stop # Shut the VM down
nomarchy-windows-vm status # Show current state
```
Requires KVM (`/dev/kvm`) and Docker (enable via `nomarchy.system.virtualization.docker`).
## Troubleshooting ## Troubleshooting
```bash ```bash
@@ -325,6 +366,7 @@ nomarchy-debug --no-sudo --print
nomarchy-upload-log nomarchy-upload-log
# Reset specific config to defaults # Reset specific config to defaults
# Examples: nomarchy-refresh-fastfetch, nomarchy-refresh-hyprland, nomarchy-refresh-waybar
nomarchy-refresh-<app> nomarchy-refresh-<app>
# Refresh specific config file # Refresh specific config file
@@ -352,7 +394,7 @@ When user requests system changes:
This skill intentionally does not cover Nomarchy source development. Do not use this skill for: This skill intentionally does not cover Nomarchy source development. Do not use this skill for:
- Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.) - Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.)
- Creating or editing migrations - Creating or editing migrations
- Running `nomarchy-dev-*` commands - Modifying Nomarchy's own source tree
## Example Requests ## Example Requests

View File

@@ -3,5 +3,4 @@ export SUDO_EDITOR="$EDITOR"
export BAT_THEME=ansi export BAT_THEME=ansi
# Duplicated from .config/uwsm/env so SSH works too # Duplicated from .config/uwsm/env so SSH works too
export NOMARCHY_PATH=$HOME/.local/share/nomarchy export PATH=$PATH:$HOME/.local/bin
export PATH=$NOMARCHY_PATH/bin:$PATH:$HOME/.local/bin

View File

@@ -1,11 +0,0 @@
# If not running interactively, don't do anything (leave this at the top of this file)
[[ $- != *i* ]] && return
# All the default Nomarchy aliases and functions
# (don't mess with these directly, just overwrite them here!)
source ~/.config/nomarchy/default/bash/rc
# Add your own exports, aliases, and functions here.
#
# Make an alias for invoking commands you use constantly
# alias p='python'

View File

@@ -32,8 +32,7 @@ end
function GetEntries() function GetEntries()
local entries = {} local entries = {}
local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes" local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes"
local nomarchy_path = os.getenv("NOMARCHY_PATH") or "" local default_theme_dir = os.getenv("HOME") .. "/.local/share/nomarchy/themes"
local default_theme_dir = nomarchy_path .. "/themes"
local seen_themes = {} local seen_themes = {}

View File

@@ -1,8 +1,18 @@
# App-specific tweaks # App-specific tweaks. All rules are class- or title-gated, so sourcing
# unconditionally is harmless when the app isn't installed or running.
source = ~/.config/nomarchy/default/hypr/apps/1password.conf
source = ~/.config/nomarchy/default/hypr/apps/bitwarden.conf
source = ~/.config/nomarchy/default/hypr/apps/browser.conf source = ~/.config/nomarchy/default/hypr/apps/browser.conf
source = ~/.config/nomarchy/default/hypr/apps/davinci-resolve.conf
source = ~/.config/nomarchy/default/hypr/apps/geforce.conf
source = ~/.config/nomarchy/default/hypr/apps/hyprshot.conf source = ~/.config/nomarchy/default/hypr/apps/hyprshot.conf
source = ~/.config/nomarchy/default/hypr/apps/localsend.conf source = ~/.config/nomarchy/default/hypr/apps/localsend.conf
source = ~/.config/nomarchy/default/hypr/apps/moonlight.conf
source = ~/.config/nomarchy/default/hypr/apps/pip.conf
source = ~/.config/nomarchy/default/hypr/apps/qemu.conf
source = ~/.config/nomarchy/default/hypr/apps/retroarch.conf source = ~/.config/nomarchy/default/hypr/apps/retroarch.conf
source = ~/.config/nomarchy/default/hypr/apps/steam.conf
source = ~/.config/nomarchy/default/hypr/apps/system.conf
source = ~/.config/nomarchy/default/hypr/apps/telegram.conf source = ~/.config/nomarchy/default/hypr/apps/telegram.conf
source = ~/.config/nomarchy/default/hypr/apps/terminals.conf source = ~/.config/nomarchy/default/hypr/apps/terminals.conf
source = ~/.config/nomarchy/default/hypr/apps/walker.conf source = ~/.config/nomarchy/default/hypr/apps/walker.conf

View File

@@ -1,7 +1,9 @@
exec-once = uwsm-app -- hypridle exec-once = uwsm-app -- hypridle
exec-once = uwsm-app -- mako exec-once = uwsm-app -- mako
exec-once = nomarchy-welcome
# exec-once = uwsm-app -- waybar # exec-once = uwsm-app -- waybar
exec-once = uwsm-app -- fcitx5 --disable notificationitem # fcitx5 is autostarted by NixOS's i18n.inputMethod when
# nomarchy.system.inputMethod.enable = true; no manual exec-once needed.
# swaybg is started as a systemd user service (nomarchy-wallpaper.service) # swaybg is started as a systemd user service (nomarchy-wallpaper.service)
# so failures surface in logs and the wallpaper survives Hyprland restarts. # so failures surface in logs and the wallpaper survives Hyprland restarts.
exec-once = uwsm-app -- swayosd-server exec-once = uwsm-app -- swayosd-server
@@ -10,3 +12,6 @@ exec-once = nomarchy-on-boot
# Slow app launch fix -- set systemd vars # Slow app launch fix -- set systemd vars
exec-once = systemctl --user import-environment $(env | cut -d'=' -f 1) exec-once = systemctl --user import-environment $(env | cut -d'=' -f 1)
exec-once = dbus-update-activation-environment --systemd --all exec-once = dbus-update-activation-environment --systemd --all
# Network Manager
exec-once = uwsm-app 'nm-applet --indicator'

View File

@@ -1,16 +0,0 @@
# Deprecated bindings file. New installations include everything directly.
bindd = SUPER, RETURN, Terminal, exec, $terminal
bindd = SUPER, F, File manager, exec, $fileManager
bindd = SUPER, B, Web browser, exec, $browser
bindd = SUPER, M, Music player, exec, $music
bindd = SUPER, N, Neovim, exec, $terminal -e nvim
bindd = SUPER, T, Top, exec, $terminal -e btop
bindd = SUPER, D, Lazy Docker, exec, $terminal -e lazydocker
bindd = SUPER, G, Messenger, exec, $messenger
bindd = SUPER, O, Obsidian, exec, obsidian -disable-gpu
bindd = SUPER, SLASH, Password manager, exec, $passwordManager
source = ~/.config/nomarchy/default/hypr/bindings/media.conf
source = ~/.config/nomarchy/default/hypr/bindings/tiling.conf
source = ~/.config/nomarchy/default/hypr/bindings/utilities.conf

View File

@@ -1,74 +0,0 @@
# This is now a deprecated file meant for those who did not wish to learn the latest Nomarchy hotkeys.
# Do not make changes here, but bring them to tiling-v2.conf instead.
# Close windows
bindd = SUPER, W, Close window, killactive,
bindd = CTRL ALT, DELETE, Close all windows, exec, nomarchy-hyprland-window-close-all
# Control tiling
bindd = SUPER, J, Toggle window split, togglesplit, # dwindle
bindd = SUPER, P, Pseudo window, pseudo, # dwindle
bindd = SUPER SHIFT, V, Toggle window floating/tiling, togglefloating,
bindd = SHIFT, F11, Force full screen, fullscreen, 0
bindd = ALT, F11, Full width, fullscreen, 1
# Move focus with SUPER + arrow keys
bindd = SUPER, LEFT, Move focus left, movefocus, l
bindd = SUPER, RIGHT, Move focus right, movefocus, r
bindd = SUPER, UP, Move focus up, movefocus, u
bindd = SUPER, DOWN, Move focus down, movefocus, d
# Switch workspaces with SUPER + [0-9]
bindd = SUPER, code:10, Switch to workspace 1, workspace, 1
bindd = SUPER, code:11, Switch to workspace 2, workspace, 2
bindd = SUPER, code:12, Switch to workspace 3, workspace, 3
bindd = SUPER, code:13, Switch to workspace 4, workspace, 4
bindd = SUPER, code:14, Switch to workspace 5, workspace, 5
bindd = SUPER, code:15, Switch to workspace 6, workspace, 6
bindd = SUPER, code:16, Switch to workspace 7, workspace, 7
bindd = SUPER, code:17, Switch to workspace 8, workspace, 8
bindd = SUPER, code:18, Switch to workspace 9, workspace, 9
bindd = SUPER, code:19, Switch to workspace 10, workspace, 10
# Move active window to a workspace with SUPER + SHIFT + [0-9]
bindd = SUPER SHIFT, code:10, Move window to workspace 1, movetoworkspace, 1
bindd = SUPER SHIFT, code:11, Move window to workspace 2, movetoworkspace, 2
bindd = SUPER SHIFT, code:12, Move window to workspace 3, movetoworkspace, 3
bindd = SUPER SHIFT, code:13, Move window to workspace 4, movetoworkspace, 4
bindd = SUPER SHIFT, code:14, Move window to workspace 5, movetoworkspace, 5
bindd = SUPER SHIFT, code:15, Move window to workspace 6, movetoworkspace, 6
bindd = SUPER SHIFT, code:16, Move window to workspace 7, movetoworkspace, 7
bindd = SUPER SHIFT, code:17, Move window to workspace 8, movetoworkspace, 8
bindd = SUPER SHIFT, code:18, Move window to workspace 9, movetoworkspace, 9
bindd = SUPER SHIFT, code:19, Move window to workspace 10, movetoworkspace, 10
# TAB between workspaces
bindd = SUPER, TAB, Next workspace, workspace, e+1
bindd = SUPER SHIFT, TAB, Previous workspace, workspace, e-1
bindd = SUPER CTRL, TAB, Former workspace, workspace, previous
# Swap active window with the one next to it with SUPER + SHIFT + arrow keys
bindd = SUPER SHIFT, LEFT, Swap window to the left, swapwindow, l
bindd = SUPER SHIFT, RIGHT, Swap window to the right, swapwindow, r
bindd = SUPER SHIFT, UP, Swap window up, swapwindow, u
bindd = SUPER SHIFT, DOWN, Swap window down, swapwindow, d
# Cycle through applications on active workspace
bindd = ALT, TAB, Cycle to next window, cyclenext
bindd = ALT SHIFT, TAB, Cycle to prev window, cyclenext, prev
bindd = ALT, TAB, Reveal active window on top, bringactivetotop
bindd = ALT SHIFT, TAB, Reveal active window on top, bringactivetotop
# Resize active window
bindd = SUPER, code:20, Expand window left, resizeactive, -100 0 # - key
bindd = SUPER, code:21, Shrink window left, resizeactive, 100 0 # = key
bindd = SUPER SHIFT, code:20, Shrink window up, resizeactive, 0 -100
bindd = SUPER SHIFT, code:21, Expand window down, resizeactive, 0 100
# Scroll through existing workspaces with SUPER + scroll
bindd = SUPER, MOUSE_DOWN, Scroll active workspace forward, workspace, e+1
bindd = SUPER, MOUSE_UP, Scroll active workspace backward, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and dragging
bindmd = SUPER, mouse:272, Move window, movewindow
bindmd = SUPER, mouse:273, Resize window, resizewindow

View File

@@ -1,16 +0,0 @@
# Application bindings
bindd = SUPER, RETURN, Terminal, exec, uwsm-app -- xdg-terminal-exec --dir="$(nomarchy-cmd-terminal-cwd)"
bindd = SUPER SHIFT, RETURN, Browser, exec, nomarchy-launch-browser
bindd = SUPER SHIFT, F, File manager, exec, uwsm-app -- nautilus --new-window
bindd = SUPER ALT SHIFT, F, File manager (cwd), exec, uwsm-app -- nautilus --new-window "$(nomarchy-cmd-terminal-cwd)"
bindd = SUPER SHIFT, B, Browser, exec, nomarchy-launch-browser
bindd = SUPER SHIFT ALT, B, Browser (private), exec, nomarchy-launch-browser --private
bindd = SUPER SHIFT, N, Editor, exec, nomarchy-launch-editor
# Add extra bindings
# bindd = SUPER SHIFT, A, ChatGPT, exec, nomarchy-launch-webapp "https://chatgpt.com"
# bindd = SUPER SHIFT, R, exec, alacritty -e ssh your-server
# Overwrite existing bindings, like putting Nomarchy Menu on Super + Space
# unbind = SUPER, SPACE
# bindd = SUPER, SPACE, Nomarchy menu, exec, nomarchy-menu

View File

@@ -1,268 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--The Xournal++ settings file. Do not edit this file! Most settings are available in the Settings dialog, the others are commented in this file, but handle with care!-->
<settings>
<property name="pressureSensitivity" value="true"/>
<property name="minimumPressure" value="0.05"/>
<property name="pressureMultiplier" value="1"/>
<property name="zoomGesturesEnabled" value="true"/>
<property name="selectedToolbar" value="Portrait"/>
<property name="lastSavePath" value="/home/dhh/Downloads"/>
<property name="lastOpenPath" value=""/>
<property name="lastImagePath" value="/home/dhh/Dropbox/Images"/>
<property name="edgePanSpeed" value="20"/>
<property name="edgePanMaxMult" value="5"/>
<property name="zoomStep" value="10"/>
<property name="zoomStepScroll" value="2"/>
<property name="displayDpi" value="72"/>
<property name="mainWndWidth" value="800"/>
<property name="mainWndHeight" value="600"/>
<property name="maximized" value="true"/>
<property name="showToolbar" value="true"/>
<property name="showSidebar" value="true"/>
<property name="sidebarWidth" value="150"/>
<property name="sidebarNumberingStyle" value="1"/>
<property name="sidebarOnRight" value="false"/>
<property name="scrollbarOnLeft" value="false"/>
<property name="menubarVisible" value="true"/>
<property name="filepathShownInTitlebar" value="false"/>
<property name="numColumns" value="1"/>
<property name="numRows" value="1"/>
<property name="viewFixedRows" value="false"/>
<property name="showPairedPages" value="false"/>
<property name="layoutVertical" value="false"/>
<property name="layoutRightToLeft" value="false"/>
<property name="layoutBottomToTop" value="false"/>
<property name="numPairsOffset" value="1"/>
<!--The icon theme, allowed values are "disabled", "onDrawOfLastPage", and "onScrollOfLastPage"-->
<property name="emptyLastPageAppend" value="disabled"/>
<property name="presentationMode" value="false"/>
<!--Which GUI elements are shown in default view mode, separated by a colon (,)-->
<property name="defaultViewModeAttributes" value="showMenubar,showToolbar,showSidebar"/>
<!--Which GUI elements are shown in fullscreen view mode, separated by a colon (,)-->
<property name="fullscreenViewModeAttributes" value="goFullscren,showToolbar,showSidebar"/>
<!--Which GUI elements are shown in presentation view mode, separated by a colon (,)-->
<property name="presentationViewModeAttributes" value="goFullscren,showToolbar"/>
<!--The cursor icon used with a stylus, allowed values are "none", "dot", "big", "arrow"-->
<property name="stylusCursorType" value="dot"/>
<!--The eraser cursor visibility used with a stylus, allowed values are "never", "always", "hover", "touch"-->
<property name="eraserVisibility" value="always"/>
<!--The icon theme, allowed values are "iconsColor", "iconsLucide"-->
<property name="iconTheme" value="iconsColor"/>
<!--Dark/light mode, allowed values are "useSystem", "forceLight", "forceDark"-->
<property name="themeVariant" value="forceDark"/>
<property name="highlightPosition" value="false"/>
<property name="cursorHighlightColor" value="2164260608"/>
<property name="cursorHighlightBorderColor" value="2147483903"/>
<property name="cursorHighlightRadius" value="30"/>
<property name="cursorHighlightBorderWidth" value="0"/>
<property name="useStockIcons" value="false"/>
<property name="disableScrollbarFadeout" value="false"/>
<property name="disableAudio" value="false"/>
<!--Hides scroolbars in the main window, allowed values: "none", "horizontal", "vertical", "both"-->
<property name="scrollbarHideType" value="none"/>
<property name="autoloadMostRecent" value="false"/>
<property name="autoloadPdfXoj" value="true"/>
<property name="defaultSaveName" value="%F-Note-%H-%M"/>
<property name="defaultPdfExportName" value="%{name}_annotated"/>
<property name="autosaveEnabled" value="true"/>
<property name="autosaveTimeout" value="3"/>
<property name="addHorizontalSpace" value="false"/>
<property name="addHorizontalSpaceAmount" value="150"/>
<property name="addVerticalSpace" value="false"/>
<property name="addVerticalSpaceAmount" value="150"/>
<property name="drawDirModsEnabled" value="false"/>
<property name="drawDirModsRadius" value="50"/>
<property name="snapRotation" value="true"/>
<property name="snapRotationTolerance" value="0.3"/>
<property name="snapGrid" value="true"/>
<property name="snapGridTolerance" value="0.5"/>
<property name="snapGridSize" value="14.17"/>
<property name="strokeRecognizerMinSize" value="40"/>
<property name="touchDrawing" value="false"/>
<property name="gtkTouchInertialScrolling" value="true"/>
<property name="pressureGuessing" value="false"/>
<property name="selectionBorderColor" value="4294901760"/>
<property name="backgroundColor" value="4292664021"/>
<property name="selectionMarkerColor" value="4285702095"/>
<property name="touchZoomStartThreshold" value="0"/>
<property name="pageRerenderThreshold" value="5"/>
<!--The count of rendered PDF pages which will be cached.-->
<property name="pdfPageCacheSize" value="10"/>
<property name="preloadPagesBefore" value="3"/>
<property name="preloadPagesAfter" value="5"/>
<property name="eagerPageCleanup" value="true"/>
<!--Config for new pages-->
<property name="pageTemplate" value="xoj/template&#10;copyLastPageSettings=true&#10;size=595.275591x841.889764&#10;backgroundType=lined&#10;backgroundColor=#ffffff&#10;"/>
<property name="sizeUnit" value=""/>
<property name="audioFolder" value=""/>
<property name="audioInputDevice" value="-1"/>
<property name="audioOutputDevice" value="-1"/>
<property name="audioSampleRate" value="44100"/>
<property name="audioGain" value="1"/>
<property name="defaultSeekTime" value="5"/>
<property name="pluginEnabled" value=""/>
<property name="pluginDisabled" value=""/>
<property name="strokeFilterIgnoreTime" value="150"/>
<property name="strokeFilterIgnoreLength" value="1"/>
<property name="strokeFilterSuccessiveTime" value="500"/>
<property name="strokeFilterEnabled" value="false"/>
<property name="doActionOnStrokeFiltered" value="false"/>
<property name="trySelectOnStrokeFiltered" value="false"/>
<property name="snapRecognizedShapesEnabled" value="false"/>
<property name="restoreLineWidthEnabled" value="false"/>
<property name="numIgnoredStylusEvents" value="0"/>
<property name="inputSystemTPCButton" value="false"/>
<property name="inputSystemDrawOutsideWindow" value="true"/>
<property name="preferredLocale" value=""/>
<property name="stabilizerAveragingMethod" value="0"/>
<property name="stabilizerPreprocessor" value="0"/>
<property name="stabilizerBuffersize" value="20"/>
<property name="stabilizerSigma" value="0.5"/>
<property name="stabilizerDeadzoneRadius" value="1.3"/>
<property name="stabilizerDrag" value="0.4"/>
<property name="stabilizerMass" value="5"/>
<property name="stabilizerCuspDetection" value="true"/>
<property name="stabilizerFinalizeStroke" value="true"/>
<property name="latexSettings.autoCheckDependencies" value="true"/>
<property name="latexSettings.defaultText" value="x^2"/>
<property name="latexSettings.globalTemplatePath" value="/usr/share/xournalpp/resources/default_template.tex"/>
<property name="latexSettings.genCmd" value="pdflatex -halt-on-error -interaction=nonstopmode '{}'"/>
<property name="latexSettings.sourceViewThemeId" value=""/>
<property name="latexSettings.editorFont" value="Monospace 12"/>
<property name="latexSettings.useCustomEditorFont" value="false"/>
<property name="latexSettings.editorWordWrap" value="true"/>
<property name="latexSettings.sourceViewAutoIndent" value="true"/>
<property name="latexSettings.sourceViewSyntaxHighlight" value="true"/>
<property name="latexSettings.sourceViewShowLineNumbers" value="false"/>
<property name="font" font="Sans" size="12"/>
<data name="buttonConfig">
<data name="default">
<attribute name="color" type="hex" value="ff000000"/>
<attribute name="drawingType" type="string" value="default"/>
<attribute name="size" type="string" value="thin"/>
<attribute name="tool" type="string" value="pen"/>
</data>
<data name="eraser">
<attribute name="eraserMode" type="string" value="none"/>
<attribute name="size" type="string" value="none"/>
<attribute name="tool" type="string" value="eraser"/>
</data>
<data name="middle">
<attribute name="tool" type="string" value="hand"/>
</data>
<data name="right">
<attribute name="tool" type="string" value="none"/>
</data>
<data name="stylus">
<attribute name="tool" type="string" value="none"/>
</data>
<data name="stylus2">
<attribute name="tool" type="string" value="none"/>
</data>
<data name="touch">
<attribute name="device" type="string" value=""/>
<attribute name="disableDrawing" type="boolean" value="false"/>
<attribute name="tool" type="string" value="none"/>
</data>
</data>
<data name="deviceClasses">
<data name="Wayland Finger Scrolling">
<attribute name="deviceClass" type="int" value="1"/>
<attribute name="deviceSource" type="int" value="6"/>
</data>
<data name="Wayland Pointer">
<attribute name="deviceClass" type="int" value="1"/>
<attribute name="deviceSource" type="int" value="0"/>
</data>
</data>
<data name="tools">
<attribute name="current" type="string" value="text"/>
<data name="drawArrow">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="drawCoordinateSystem">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="drawDoubleArrow">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="drawEllipse">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="drawRect">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="drawSpline">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="eraser">
<attribute name="drawingType" type="string" value="default"/>
<attribute name="size" type="string" value="MEDIUM"/>
<attribute name="type" type="string" value="default"/>
</data>
<data name="hand">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="highlighter">
<attribute name="color" type="hex" value="ffffff00"/>
<attribute name="drawingType" type="string" value="default"/>
<attribute name="fill" type="int" value="0"/>
<attribute name="fillAlpha" type="int" value="128"/>
<attribute name="size" type="string" value="MEDIUM"/>
</data>
<data name="image">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="pen">
<attribute name="color" type="hex" value="ff3333cc"/>
<attribute name="drawingType" type="string" value="default"/>
<attribute name="fill" type="int" value="0"/>
<attribute name="fillAlpha" type="int" value="128"/>
<attribute name="size" type="string" value="MEDIUM"/>
<attribute name="style" type="string" value="plain"/>
</data>
<data name="playObject">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="selectMultiLayerRect">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="selectMultiLayerRegion">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="selectObject">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="selectPdfTextLinear">
<attribute name="color" type="hex" value="ff000000"/>
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="selectPdfTextRect">
<attribute name="color" type="hex" value="ff000000"/>
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="selectRect">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="selectRegion">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="showFloatingToolbox">
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="text">
<attribute name="color" type="hex" value="ff000000"/>
<attribute name="drawingType" type="string" value="default"/>
</data>
<data name="verticalSpace">
<attribute name="drawingType" type="string" value="default"/>
</data>
</data>
<data name="touch">
<attribute name="cmdDisable" type="string" value=""/>
<attribute name="cmdEnable" type="string" value=""/>
<attribute name="disableTouch" type="boolean" value="false"/>
<attribute name="method" type="string" value="auto"/>
<attribute name="timeout" type="int" value="1000"/>
</data>
</settings>

View File

@@ -7,7 +7,6 @@ let
# This replaces dynamic builtins.readDir for clarity and faster evaluation # This replaces dynamic builtins.readDir for clarity and faster evaluation
configItems = { configItems = {
# Directories # Directories
"environment.d" = "directory";
fastfetch = "directory"; fastfetch = "directory";
fcitx5 = "directory"; fcitx5 = "directory";
fontconfig = "directory"; fontconfig = "directory";
@@ -16,10 +15,8 @@ let
"nautilus-python" = "directory"; "nautilus-python" = "directory";
nomarchy = "directory"; nomarchy = "directory";
"nomarchy-skill" = "directory"; "nomarchy-skill" = "directory";
Typora = "directory";
uwsm = "directory"; uwsm = "directory";
wiremix = "directory"; wiremix = "directory";
xournalpp = "directory";
# Files # Files
"brave-flags.conf" = "regular"; "brave-flags.conf" = "regular";
@@ -54,5 +51,17 @@ let
in in
{ {
xdg.configFile = configMappings; xdg.configFile = configMappings // {
# mako reads ~/.config/mako/config by default. The themed Nomarchy
# config (urgency rules, app filters, button handlers) lives under
# nomarchy/default/mako/core.ini for organizational reasons, so wire
# it explicitly here. Without this, mako silently falls back to its
# built-in defaults and every Nomarchy notification customization is
# inert.
"mako/config".source = lib.mkDefault ./config/nomarchy/default/mako/core.ini;
};
home.file.".XCompose" = lib.mkDefault {
source = ./config/nomarchy/default/xcompose;
};
} }

View File

@@ -5,10 +5,10 @@
./options.nix ./options.nix
./state.nix ./state.nix
./overrides.nix ./overrides.nix
./behavior.nix
./fonts.nix ./fonts.nix
./configs.nix ./configs.nix
./security.nix ./security.nix
./bash.nix ./bash.nix
./gaming.nix
]; ];
} }

12
core/home/gaming.nix Normal file
View File

@@ -0,0 +1,12 @@
{ config, lib, ... }:
let
cfg = config.nomarchy.gaming;
in
{
config = lib.mkIf cfg.enable {
wayland.windowManager.hyprland.extraConfig = ''
windowrulev2 = fullscreen, class:^(steam_app_).*$
'';
};
}

View File

@@ -1,75 +1,92 @@
{ lib, pkgs, ... }: { lib, pkgs, ... }:
let
# Defaults live in lib/state-schema.nix so they can't drift between this
# file, core/system/options.nix, and core/home/state.nix's `or` fallbacks.
schema = import ../../lib/state-schema.nix { inherit lib; };
in
{ {
options.nomarchy = { options.nomarchy = {
toggles = { toggles = {
suspend = lib.mkOption { suspend = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.suspend;
description = "Whether to show suspend in system menu."; description = "Whether to show suspend in system menu.";
}; };
screensaver = lib.mkOption { screensaver = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.screensaver;
description = "Whether the screensaver is enabled."; description = "Whether the screensaver is enabled.";
}; };
idle = lib.mkOption { idle = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.idle;
description = "Whether the idle lock is enabled."; description = "Whether the idle lock is enabled.";
}; };
nightlight = lib.mkOption { nightlight = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.home.nightlight;
description = "Whether the nightlight is enabled."; description = "Whether the nightlight is enabled.";
}; };
waybar = lib.mkOption { waybar = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.waybar;
description = "Whether the top bar is enabled."; description = "Whether the top bar is enabled.";
}; };
skipVsCodeTheme = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to skip theme changes in VSCode.";
};
}; };
nightlightTemperature = lib.mkOption { nightlightTemperature = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 4000; default = schema.home.nightlightTemperature;
description = "Temperature for the nightlight."; description = "Temperature for the nightlight.";
}; };
theme = lib.mkOption { theme = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "summer-night"; default = schema.home.theme;
description = "System theme name."; description = "System theme name.";
}; };
formFactor = lib.mkOption {
type = lib.types.enum [ "laptop" "desktop" ];
default = "laptop";
description = ''
Physical form factor. Drives UI affordances (battery widget,
future lid handling). Default "laptop" battery widget is
harmless on a desktop (renders empty when no BAT* is present),
so the safe default is "show, don't hide". The installer
auto-detects via /sys/class/power_supply/BAT* and writes the
explicit value into the generated home.nix.
'';
};
wallpaper = lib.mkOption { wallpaper = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = ""; default = schema.home.wallpaper;
description = "System wallpaper path."; description = "System wallpaper path.";
}; };
panelPosition = lib.mkOption {
type = lib.types.enum [ "top" "bottom" ];
default = schema.home.panelPosition;
description = "Waybar panel position.";
};
hyprland = { hyprland = {
gaps_in = lib.mkOption { gaps_in = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 5; default = schema.home.hyprland.gaps_in;
description = "Inner gaps for Hyprland."; description = "Inner gaps for Hyprland.";
}; };
gaps_out = lib.mkOption { gaps_out = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 10; default = schema.home.hyprland.gaps_out;
description = "Outer gaps for Hyprland."; description = "Outer gaps for Hyprland.";
}; };
border_size = lib.mkOption { border_size = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 2; default = schema.home.hyprland.border_size;
description = "Border size for Hyprland."; description = "Border size for Hyprland.";
}; };
}; };
fonts = { fonts = {
monospace = lib.mkOption { monospace = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "JetBrainsMono Nerd Font"; default = schema.home.font;
description = "System monospace font."; description = "System monospace font.";
}; };
}; };
@@ -100,5 +117,27 @@
default = null; default = null;
description = "Path to a directory containing configuration overrides."; description = "Path to a directory containing configuration overrides.";
}; };
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.
'';
};
};
gaming = {
enable = lib.mkEnableOption ''
Gaming preset (home-side companion to nomarchy.system.gaming.enable).
Adds a Hyprland window rule that fullscreens windows whose class
matches steam_app_* i.e. games launched through Steam so they
grab the whole screen instead of opening windowed. Set this to the
same value as nomarchy.system.gaming.enable; the installer flips
both when the Gaming profile is selected.
'';
};
}; };
} }

View File

@@ -1,97 +1,34 @@
{ config, lib, ... }: { config, lib, ... }:
# File-based override system for Nomarchy # File-based override system for Nomarchy.
# #
# Users can place config files in ~/.config/nomarchy/overrides/ to completely # STATUS: option surface only — the actual override mechanism is NOT yet
# replace upstream defaults. Override priority (highest to lowest): # implemented. The options are kept so configs that already set
# 1. User Nix options # `nomarchy.overrides.enable = …;` continue to evaluate; setting them has
# 2. User file overrides (~/.config/nomarchy/overrides/) # no effect today. Tracked in docs/ROADMAP.md.
# 3. Upstream defaults
# #
# Supported override paths: # When implemented, this module should substitute sources in
# - hypr/ - Hyprland configs (bindings.conf, input.conf, etc.) # `xdg.configFile.<path>.source` based on the presence of matching files
# - waybar/ - Waybar config and style # under ~/.config/nomarchy/overrides/.
# - alacritty/ - Alacritty terminal config
# - walker/ - Walker launcher config
# - kitty/ - Kitty terminal config
# - btop/ - Btop resource monitor config
# - apps/ - Other application configs
let
overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides";
# Helper to get override from options
getOverrideOrDefault = { path, default }:
config.nomarchy.overrides.paths.${path} or default;
in
{ {
options.nomarchy.overrides = { options.nomarchy.overrides = {
enable = lib.mkOption { enable = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = true;
description = "Whether to enable file-based override loading from ~/.config/nomarchy/overrides/"; description = ''
Reserved for the future file-based override loader. Currently a
no-op setting this has no effect. See docs/ROADMAP.md.
'';
}; };
paths = lib.mkOption { paths = lib.mkOption {
type = lib.types.attrsOf lib.types.path; type = lib.types.attrsOf lib.types.path;
default = {}; default = {};
description = "Override paths discovered at build time. Populated by the override system."; description = ''
}; Reserved for the future file-based override loader. Currently
}; unused.
config = lib.mkIf config.nomarchy.overrides.enable {
# Create the overrides directory structure if it doesn't exist
home.activation.createOverridesDir = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p "${overridesDir}"
mkdir -p "${overridesDir}/hypr"
mkdir -p "${overridesDir}/waybar"
mkdir -p "${overridesDir}/apps"
'';
# Document the override system
xdg.configFile."nomarchy/overrides/README.md".text = lib.mkDefault ''
# Nomarchy Configuration Overrides
Place files in this directory to override upstream Nomarchy defaults.
## Directory Structure
```
overrides/
hypr/
bindings.conf # Keybindings
input.conf # Input settings
monitors.conf # Monitor layout
rules.conf # Window rules
autostart.conf # Startup apps
waybar/
config.jsonc # Waybar layout
style.css # Waybar styling
apps/
alacritty.toml # Terminal behavior
...
README.md # This file
```
## Override Priority
1. **Nix Options** (highest) - Set in your flake/config
2. **File Overrides** - Files in this directory
3. **Upstream Defaults** (lowest) - Nomarchy defaults
## Usage
1. Copy the file you want to customize from the upstream config
2. Place it in the appropriate directory here
3. Edit to your preferences
4. Run `nixos-rebuild switch` or `home-manager switch`
## Tips
- For keybindings, copy `~/.config/hypr/bindings.conf` to `overrides/hypr/`
- For Waybar styling, copy `~/.config/waybar/style.css` to `overrides/waybar/`
- Changes here persist across Nomarchy updates
''; '';
}; };
};
} }

View File

@@ -2,41 +2,51 @@
let let
nomarchyLib = import ../../lib { inherit lib; }; nomarchyLib = import ../../lib { inherit lib; };
# Single source of truth for default values when state.json is missing
# a key. Both core/system/options.nix and core/home/options.nix read
# from this same file — changing a default in one place updates
# everywhere. (Was: each consumer hardcoded its own `or X` literal,
# which is how the summer-night/nord split lived for so long.)
schema = import ../../lib/state-schema.nix { inherit lib; };
assetsPath = ../../themes/palettes; assetsPath = ../../themes/palettes;
# Read unified state from ~/.config/nomarchy/state.json # Read unified state from ~/.config/nomarchy/state.json
togglesState = nomarchyLib.readHomeState config.home.homeDirectory; togglesState = nomarchyLib.readHomeState config.home.homeDirectory;
in 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.
config = { config = {
nomarchy = { nomarchy = {
toggles = { toggles = {
suspend = togglesState.suspend or true; suspend = lib.mkDefault (togglesState.suspend or schema.home.suspend);
screensaver = togglesState.screensaver or true; screensaver = lib.mkDefault (togglesState.screensaver or schema.home.screensaver);
idle = togglesState.idle or true; idle = lib.mkDefault (togglesState.idle or schema.home.idle);
nightlight = togglesState.nightlight or false; nightlight = lib.mkDefault (togglesState.nightlight or schema.home.nightlight);
waybar = togglesState.waybar or true; waybar = lib.mkDefault (togglesState.waybar or schema.home.waybar);
skipVsCodeTheme = togglesState.skipVsCodeTheme or false;
}; };
nightlightTemperature = togglesState.nightlightTemperature or 4000; nightlightTemperature = lib.mkDefault (togglesState.nightlightTemperature or schema.home.nightlightTemperature);
theme = togglesState.theme or "summer-night"; theme = lib.mkDefault (togglesState.theme or schema.home.theme);
wallpaper = togglesState.wallpaper or ""; wallpaper = lib.mkDefault (togglesState.wallpaper or schema.home.wallpaper);
panelPosition = lib.mkDefault (togglesState.panelPosition or schema.home.panelPosition);
hyprland = { hyprland = {
gaps_in = togglesState.hyprland.gaps_in or 5; gaps_in = lib.mkDefault (togglesState.hyprland.gaps_in or schema.home.hyprland.gaps_in);
gaps_out = togglesState.hyprland.gaps_out or 10; gaps_out = lib.mkDefault (togglesState.hyprland.gaps_out or schema.home.hyprland.gaps_out);
border_size = togglesState.hyprland.border_size or 2; border_size = lib.mkDefault (togglesState.hyprland.border_size or schema.home.hyprland.border_size);
}; };
fonts.monospace = togglesState.font or "JetBrainsMono Nerd Font"; fonts.monospace = lib.mkDefault (togglesState.font or schema.home.font);
# Derived properties from the theme directory # Derived properties from the theme directory
isLightMode = nomarchyLib.isThemeLightMode { isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or "summer-night"; themeName = togglesState.theme or schema.home.theme;
inherit assetsPath; inherit assetsPath;
}; });
iconsTheme = nomarchyLib.getIconsTheme { iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme {
themeName = togglesState.theme or "summer-night"; themeName = togglesState.theme or schema.home.theme;
inherit assetsPath; inherit assetsPath;
}; });
}; };
}; };
} }

View File

@@ -0,0 +1,14 @@
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system.accessibility;
in
{
config = lib.mkIf cfg.enable {
services.gnome.at-spi2-core.enable = lib.mkDefault true;
environment.systemPackages = [ pkgs.orca ];
environment.variables.XCURSOR_SIZE = toString cfg.cursorSize;
};
}

View File

@@ -1,4 +1,4 @@
{ ... }: { lib, ... }:
{ {
# Identify the distribution as Nomarchy in /etc/os-release. # Identify the distribution as Nomarchy in /etc/os-release.
@@ -8,4 +8,10 @@
distroId = "nomarchy"; distroId = "nomarchy";
distroName = "Nomarchy"; distroName = "Nomarchy";
}; };
# Ensure the bootloader entries use Nomarchy instead of NixOS
boot.loader.grub.configurationName = lib.mkDefault "Nomarchy";
# For systemd-boot, NixOS 24.11+ uses distroName if available,
# but some older versions or specific setups might need explicit labels.
# We use mkDefault so users can still override if they want.
} }

View File

@@ -0,0 +1,23 @@
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system.containers;
in
{
config = lib.mkIf cfg.enable {
virtualisation.podman = {
enable = true;
# `docker` and `docker-compose` invocations transparently route to
# podman. Pairs cleanly with rootless mode.
dockerCompat = true;
defaultNetwork.settings.dns_enabled = true;
};
environment.systemPackages = with pkgs; [
podman
podman-compose
podman-tui
dive
];
};
}

View File

@@ -17,12 +17,23 @@
./network.nix ./network.nix
./impermanence.nix ./impermanence.nix
./browser.nix ./browser.nix
./makima.nix # Tier 1 system features (all opt-in via nomarchy.system.*).
./snapper.nix
./laptop.nix
./desktop.nix
./accessibility.nix
./gaming.nix
./hibernate.nix
./containers.nix
./pam.nix
./input-method.nix
../../themes/engine/plymouth.nix ../../themes/engine/plymouth.nix
../../themes/engine/sddm.nix ../../themes/engine/sddm.nix
]; ];
time.timeZone = lib.mkDefault config.nomarchy.system.timezone; time.timeZone = lib.mkDefault config.nomarchy.system.timezone;
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
} }

15
core/system/desktop.nix Normal file
View File

@@ -0,0 +1,15 @@
{ config, lib, ... }:
let
cfg = config.nomarchy.system.desktop;
in
{
config = lib.mkIf cfg.enable {
powerManagement.cpuFreqGovernor = lib.mkDefault "performance";
# No-op until the user adds ZFS to boot.supportedFilesystems and a pool;
# then maintenance kicks in without further config.
services.zfs.autoScrub.enable = lib.mkDefault true;
services.zfs.trim.enable = lib.mkDefault true;
};
}

View File

@@ -5,7 +5,7 @@ let
pname = "nomarchy-font"; pname = "nomarchy-font";
version = "1.0"; version = "1.0";
# Point directly to the font file # Point directly to the font file
src = ./../home/config/Nomarchy.ttf; src = ./../branding/Nomarchy.ttf;
# No archive to unpack # No archive to unpack
unpackPhase = "true"; unpackPhase = "true";
installPhase = '' installPhase = ''

20
core/system/gaming.nix Normal file
View File

@@ -0,0 +1,20 @@
{ config, lib, ... }:
let
cfg = config.nomarchy.system.gaming;
in
{
config = lib.mkIf cfg.enable {
programs.steam = {
enable = true;
remotePlay.openFirewall = lib.mkDefault true;
localNetworkGameTransfers.openFirewall = lib.mkDefault true;
};
# gamemode adjusts CPU governor and reschedules processes when a
# game requests it. The launching user must be in the `gamemode` group.
programs.gamemode.enable = true;
services.flatpak.enable = true;
};
}

View File

@@ -10,9 +10,18 @@ in
isT2Mac = mkEnableOption "Apple T2 MacBook specific hardware fixes"; isT2Mac = mkEnableOption "Apple T2 MacBook specific hardware fixes";
isFramework = mkEnableOption "Framework laptop specific hardware fixes"; isFramework = mkEnableOption "Framework laptop specific hardware fixes";
hasIPU7Camera = mkEnableOption "Intel IPU7 camera support"; hasIPU7Camera = mkEnableOption "Intel IPU7 camera support";
fwupd = mkOption {
type = types.bool;
default = false;
description = "Whether to enable fwupd firmware update service.";
};
}; };
config = mkMerge [ config = mkMerge [
(mkIf cfg.fwupd {
services.fwupd.enable = true;
})
(mkIf cfg.isXPS { (mkIf cfg.isXPS {
services.udev.extraRules = '' services.udev.extraRules = ''
ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:00:19.0", ATTR{power/control}="on" ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:00:19.0", ATTR{power/control}="on"
@@ -61,8 +70,47 @@ in
}) })
(mkIf config.nomarchy.system.features.hybridGPU { (mkIf config.nomarchy.system.features.hybridGPU {
# supergfxd manages mode switching (Integrated / Hybrid / Vfio /
# AsusEgpu). It blacklists/unblacklists the nvidia kernel module via
# /etc/modprobe.d/ depending on the active mode. ExecStartPre sleep
# gives udev time to settle so the daemon doesn't see a half-attached
# GPU on cold boot.
services.supergfxd.enable = true; services.supergfxd.enable = true;
systemd.services.supergfxd.serviceConfig.ExecStartPre = "-${pkgs.coreutils}/bin/sleep 5"; systemd.services.supergfxd.serviceConfig.ExecStartPre = "-${pkgs.coreutils}/bin/sleep 5";
# Load the NVIDIA driver so the dGPU has something to drive. Without
# these, supergfxd switches modes successfully but the X/Wayland
# stack has no NVIDIA driver loaded — render-offload silently no-ops
# and Hyprland renders everything on the iGPU regardless of mode.
# mkDefault throughout so downstream system.nix can override
# (pin to a beta driver, flip to the open kernel module, etc.).
services.xserver.videoDrivers = lib.mkDefault [ "nvidia" ];
hardware.graphics.enable = lib.mkDefault true;
hardware.graphics.enable32Bit = lib.mkDefault true;
hardware.nvidia = {
modesetting.enable = lib.mkDefault true;
powerManagement.enable = lib.mkDefault true;
open = lib.mkDefault false;
package = lib.mkDefault config.boot.kernelPackages.nvidiaPackages.stable;
};
# Required for Wayland compositors (Hyprland) to render via NVIDIA.
boot.kernelParams = [ "nvidia-drm.modeset=1" ];
# PRIME render-offload (the part that lets `nvidia-offload <cmd>`
# actually use the dGPU) needs bus IDs, which are per-machine.
# We deliberately don't enable `hardware.nvidia.prime.offload.enable`
# here — without the correct intelBusId / nvidiaBusId the nvidia
# kernel module panics on load. The user adds this to their own
# /etc/nixos/system.nix after running `lspci -D`:
#
# hardware.nvidia.prime = {
# offload.enable = true;
# offload.enableOffloadCmd = true;
# intelBusId = "PCI:0:2:0"; # or amdgpuBusId for AMD iGPU
# nvidiaBusId = "PCI:1:0:0";
# };
#
# See docs/OPTIONS.md for the full recipe.
}) })
]; ];
} }

24
core/system/hibernate.nix Normal file
View File

@@ -0,0 +1,24 @@
{ config, lib, ... }:
let
cfg = config.nomarchy.system.hibernation;
in
{
config = lib.mkIf cfg.enable {
# Wait this long after suspend before hibernating, and use the same
# delay as the idle-action timeout so the two paths agree.
systemd.sleep.extraConfig = ''
HibernateDelaySec=${toString (cfg.idleMinutes * 60)}
'';
services.logind = {
settings.Login = {
HandleLidSwitch = lib.mkDefault "suspend-then-hibernate";
HandleLidSwitchExternalPower = lib.mkDefault "suspend";
HandlePowerKey = lib.mkDefault "hibernate";
IdleAction = lib.mkDefault "suspend-then-hibernate";
IdleActionSec = lib.mkDefault (toString (cfg.idleMinutes * 60));
};
};
};
}

View File

@@ -10,13 +10,39 @@ in
options.nomarchy.system.impermanence = { options.nomarchy.system.impermanence = {
enable = lib.mkEnableOption "Erase Your Darlings (Impermanence) root wipe on boot"; enable = lib.mkEnableOption "Erase Your Darlings (Impermanence) root wipe on boot";
# The disko layout names the main LUKS mapping `crypted` on single-disk
# installs and `crypted_main` on multi-disk installs (see
# installer/disko-config.nix: `mainLuksName`). The rollback hook must
# mount the right device, otherwise initrd fails on every boot and the
# @ → root-blank snapshot is never restored.
mainLuksName = lib.mkOption {
type = lib.types.str;
default = "crypted";
description = ''
Name of the /dev/mapper entry holding the BTRFS root. Set to
"crypted_main" on multi-disk installs to match the disko layout.
'';
};
user = lib.mkOption {
type = lib.types.str;
default = "nomarchy";
description = ''
Primary user whose home subset (.ssh, .gnupg, keyrings, common
directories) survives the rootfs wipe. Must match the user
created via `users.users.<name>` otherwise the persistence
block is silently inert and the user's home directory is wiped
on every boot. The installer writes this for you.
'';
};
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# 1. The Rollback Script: Runs in initrd before filesystems are mounted # 1. The Rollback Script: Runs in initrd before filesystems are mounted
boot.initrd.postDeviceCommands = lib.mkAfter '' boot.initrd.postDeviceCommands = lib.mkAfter ''
mkdir -p /btrfs_tmp mkdir -p /btrfs_tmp
mount -o subvol=/ /dev/mapper/crypted /btrfs_tmp mount -o subvol=/ /dev/mapper/${cfg.mainLuksName} /btrfs_tmp
if [[ -e /btrfs_tmp/@ ]]; then if [[ -e /btrfs_tmp/@ ]]; then
mkdir -p /btrfs_tmp/old_roots mkdir -p /btrfs_tmp/old_roots
@@ -58,7 +84,7 @@ in
"/etc/machine-id" "/etc/machine-id"
"/etc/supergfxd.conf" "/etc/supergfxd.conf"
]; ];
users.nomarchy = { users.${cfg.user} = {
directories = [ directories = [
".ssh" ".ssh"
".gnupg" ".gnupg"

View File

@@ -0,0 +1,22 @@
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system.inputMethod;
in
{
config = lib.mkIf cfg.enable {
# NixOS's i18n.inputMethod handles env vars (GTK_IM_MODULE, QT_IM_MODULE,
# XMODIFIERS, SDL_IM_MODULE) and the fcitx5-daemon autostart for us, so
# we don't ship a manual environment.d file or Hyprland exec-once.
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
];
};
};
}

42
core/system/laptop.nix Normal file
View File

@@ -0,0 +1,42 @@
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system.laptop;
hib = config.nomarchy.system.hibernation;
lidAction = if hib.enable then "suspend-then-hibernate" else "suspend";
in
{
config = lib.mkIf cfg.enable {
services.tlp = {
enable = lib.mkDefault true;
settings = {
CPU_SCALING_GOVERNOR_ON_AC = lib.mkDefault "performance";
CPU_SCALING_GOVERNOR_ON_BAT = lib.mkDefault "powersave";
CPU_BOOST_ON_BAT = lib.mkDefault 0;
PLATFORM_PROFILE_ON_AC = lib.mkDefault "balanced";
PLATFORM_PROFILE_ON_BAT = lib.mkDefault "low-power";
# Charge thresholds only honored on supported hardware (ThinkPad,
# some ASUS); a harmless warning is logged elsewhere.
START_CHARGE_THRESH_BAT0 = lib.mkDefault 75;
STOP_CHARGE_THRESH_BAT0 = lib.mkDefault 80;
};
};
# TLP and power-profiles-daemon both arbitrate CPU/EPP — NixOS asserts
# mutual exclusion. Opt out of the preset entirely to use PPD instead.
services.power-profiles-daemon.enable = lib.mkForce false;
services.upower.enable = lib.mkDefault true;
services.thermald.enable = lib.mkDefault cfg.thermald;
# Backlight write access for the `video` group, so the existing
# nomarchy-brightness-{display,keyboard} scripts run without root.
services.udev.packages = [ pkgs.brightnessctl ];
services.logind.settings.Login = {
HandleLidSwitch = lib.mkDefault lidAction;
HandleLidSwitchExternalPower = lib.mkDefault "suspend";
HandleLidSwitchDocked = lib.mkDefault "ignore";
};
};
}

View File

@@ -1,25 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.nomarchy.system.features.makima;
in
{
config = lib.mkIf cfg {
# If the user has makima-bin available in their overlays (as they originally used a custom package),
# this will install it. Otherwise, it will fail evaluation if not available in nixpkgs.
environment.systemPackages = [ pkgs.makima-bin ];
environment.etc."makima/AT Translated Set 2 keyboard.toml".source = ../../features/apps/makima + "/AT Translated Set 2 keyboard.toml";
systemd.services.makima = {
description = "Makima key remapping service";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = config.services.displayManager.autoLogin.user;
Environment = "MAKIMA_CONFIG=/etc/makima";
ExecStart = "${pkgs.makima-bin}/bin/makima";
Restart = "on-failure";
};
};
};
}

View File

@@ -25,4 +25,8 @@ in
DNSOverTLS=opportunistic DNSOverTLS=opportunistic
''; '';
}; };
environment.systemPackages = [
pkgs.networkmanagerapplet
];
} }

View File

@@ -11,6 +11,14 @@
# Optimize storage by hard-linking identical files # Optimize storage by hard-linking identical files
auto-optimise-store = lib.mkDefault true; auto-optimise-store = lib.mkDefault true;
# Bump the substituter download buffer from the 64 MiB default to
# 512 MiB. Large NARs (kernels, electron apps, the full NixOS
# closure on first install) overrun 64 MiB and Nix prints
# "download buffer is full" until the consumer catches up — the
# warning is harmless but slows substitution and looks like an
# error. 512 MiB comfortably covers everything in our closure.
download-buffer-size = 524288000;
}; };
# Populates NIX_PATH with the nixpkgs input from the flake. # Populates NIX_PATH with the nixpkgs input from the flake.

View File

@@ -1,55 +1,216 @@
{ lib, ... }: { config, lib, pkgs, ... }:
let
# Defaults live in lib/state-schema.nix so they can't drift between this
# file, core/home/options.nix, and core/home/state.nix's `or` fallbacks.
schema = import ../../lib/state-schema.nix { inherit lib; };
in
{ {
options.nomarchy.system = { options.nomarchy.system = {
dns = lib.mkOption { dns = lib.mkOption {
type = lib.types.enum [ "Cloudflare" "Google" "DHCP" "Custom" ]; type = lib.types.enum [ "Cloudflare" "Google" "DHCP" "Custom" ];
default = "DHCP"; default = schema.system.dns;
description = "Selected DNS provider."; description = "Selected DNS provider.";
}; };
customDns = lib.mkOption { customDns = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = []; default = schema.system.customDns;
description = "List of custom DNS servers."; description = "List of custom DNS servers.";
}; };
wifi = { wifi = {
powersave = lib.mkOption { powersave = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.system.wifi.powersave;
description = "Whether to enable wifi power saving."; description = "Whether to enable wifi power saving.";
}; };
}; };
timezone = lib.mkOption { timezone = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "UTC"; default = schema.system.timezone;
description = "System timezone."; description = "System timezone.";
}; };
formFactor = lib.mkOption {
type = lib.types.enum [ "laptop" "desktop" ];
default = "laptop";
description = ''
Physical form factor. Drives UI affordances (battery widget,
future lid handling / TLP). Default "laptop" battery widget
is harmless on a desktop (renders empty when no BAT* is
present), so the safe default is "show, don't hide". The
installer auto-detects via /sys/class/power_supply/BAT* and
writes the explicit value into the generated system.nix.
'';
};
features = { features = {
fingerprint = lib.mkOption { fingerprint = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.system.features.fingerprint;
description = "Whether to enable fingerprint support."; description = "Whether to enable fingerprint support.";
}; };
fido2 = lib.mkOption { fido2 = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.system.features.fido2;
description = "Whether to enable FIDO2 support."; description = "Whether to enable FIDO2 support.";
}; };
hybridGPU = lib.mkOption { hybridGPU = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.system.features.hybridGPU;
description = "Whether to enable hybrid GPU support (supergfxd)."; description = "Whether to enable hybrid GPU support (supergfxd).";
}; };
makima = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to enable makima key remapper.";
};
}; };
theme = lib.mkOption { theme = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "summer-night"; default = schema.system.theme;
description = "Selected system theme."; description = "Selected system theme.";
}; };
# ----- Tier 1 system features (all opt-in, no behavioural change off) ---
snapper = {
enable = lib.mkEnableOption ''
Snapper-driven BTRFS timeline snapshots of `/`. Auto-disables when
`/` isn't BTRFS. Includes a `nixos-rebuild-snap` wrapper that takes
a "Pre-rebuild" snapshot before each switch.
'';
};
hibernation = {
enable = lib.mkEnableOption ''
suspend-then-hibernate (lid close, idle, power button). NOTE: this
requires a disk swap device or swapfile sized to at least RAM
zRAM alone is not enough.
'';
idleMinutes = lib.mkOption {
type = lib.types.int;
default = 30;
description = "Idle minutes before suspend-then-hibernate fires.";
};
};
laptop = {
enable = lib.mkOption {
type = lib.types.bool;
default = config.nomarchy.system.formFactor == "laptop";
defaultText = lib.literalExpression ''config.nomarchy.system.formFactor == "laptop"'';
description = ''
Laptop power preset: TLP (with sane AC/battery governors),
`services.upower`, `services.thermald` (x86_64), a brightnessctl
udev rule, and a logind lid-switch policy. Force-disables
`services.power-profiles-daemon` (mutually exclusive with TLP).
Lid-close defers to `nomarchy.system.hibernation.enable`:
suspend-then-hibernate when on, suspend otherwise. Defaults on
when `formFactor = "laptop"`.
'';
};
thermald = lib.mkOption {
type = lib.types.bool;
default = pkgs.stdenv.hostPlatform.isx86_64;
defaultText = lib.literalExpression "pkgs.stdenv.hostPlatform.isx86_64";
description = ''
Enable `services.thermald` (Intel thermal daemon). Default true on
x86_64. Harmless no-op on AMD; gated off on aarch64.
'';
};
};
desktop = {
enable = lib.mkOption {
type = lib.types.bool;
default = config.nomarchy.system.formFactor == "desktop";
defaultText = lib.literalExpression ''config.nomarchy.system.formFactor == "desktop"'';
description = ''
Desktop preset: pins `powerManagement.cpuFreqGovernor` to
`"performance"` and enables `services.zfs.autoScrub` and
`services.zfs.trim` so a future ZFS pool gets sensible
maintenance without further config. The ZFS knobs are no-ops
until the user adds `boot.supportedFilesystems = [ "zfs" ]`
and a pool. Defaults on when `formFactor = "desktop"`. Battery
widget filtering is already handled by `formFactor` itself in
`features/desktop/waybar/default.nix`.
'';
};
};
accessibility = {
enable = lib.mkEnableOption ''
Accessibility preset: AT-SPI2 framework, the Orca screen reader
on PATH, and a larger default cursor size. Off by default
accessibility is a personal preference, not a hardware-derived
signal. The Hyprland-side keybinding to launch Orca is a
separate roadmap item.
'';
cursorSize = lib.mkOption {
type = lib.types.int;
default = 32;
description = ''
XCURSOR_SIZE when accessibility is on. NixOS default is 24;
32 is a safer floor for low-vision users. Bump to 48 if the
user explicitly asks.
'';
};
};
gaming = {
enable = lib.mkEnableOption ''
Gaming preset: Steam (with remote-play firewall holes),
gamemode (CPU governor + nice on Steam launch via the user's
gamemode group), and flatpak. NOTE: flatpak's flathub remote
is not added declaratively after first boot, run
`flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo`.
The Hyprland fullscreen-on-Steam-launch window rule is a
separate roadmap item.
'';
};
containers = {
enable = lib.mkEnableOption ''
Rootless Podman with Docker compatibility (`docker` `podman`),
plus podman-compose, podman-tui and dive.
'';
};
virtualization = {
libvirt = {
enable = lib.mkEnableOption ''
libvirt daemon + virt-manager + OVMF. The user must be in the
`libvirtd` group.
'';
};
docker = {
enable = lib.mkEnableOption ''
Docker daemon + docker-compose. The user must be in the `docker`
group.
'';
};
};
keyring = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Auto-unlock GNOME Keyring at SDDM/Hyprland login and route SSH
keys through `gcr-ssh-agent`. Default on near-universal QoL
improvement.
'';
};
};
inputMethod = {
enable = lib.mkEnableOption ''
fcitx5 input method (CJK / IME). Wires NixOS's i18n.inputMethod and
autostarts fcitx5-daemon. Adds a small footprint when enabled, so
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.
'';
};
}; };
} }

28
core/system/pam.nix Normal file
View File

@@ -0,0 +1,28 @@
{ config, lib, ... }:
let
cfg = config.nomarchy.system.keyring;
in
{
config = lib.mkIf cfg.enable {
# Auto-unlock GNOME Keyring at SDDM autologin and at login. hyprlock
# gets the same treatment so the session keyring stays unlocked when
# the screen lock disengages.
security.pam.services = {
login.enableGnomeKeyring = true;
sddm.enableGnomeKeyring = true;
hyprlock.enableGnomeKeyring = true;
};
# Run the keyring + the gcr SSH agent. Disabling `programs.ssh.startAgent`
# ensures keys flow through the keyring's agent (so unlock-on-login
# carries over to ssh) instead of a separate ssh-agent process.
services.gnome.gnome-keyring.enable = true;
services.gnome.gcr-ssh-agent.enable = true;
programs.ssh.startAgent = lib.mkForce false;
# Point downstream tooling at the gcr socket so `ssh` / `git` / etc.
# find the keyring's keys without per-user shell config.
environment.sessionVariables.SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/gcr/ssh";
};
}

View File

@@ -30,6 +30,9 @@ let
bc bc
supergfxctl supergfxctl
systemd systemd
fwupd
hyprland
swayosd
]; ];
in in
pkgs.stdenv.mkDerivation { pkgs.stdenv.mkDerivation {
@@ -52,8 +55,7 @@ pkgs.stdenv.mkDerivation {
for file in $out/bin/*; do for file in $out/bin/*; do
if [ -f "$file" ]; then if [ -f "$file" ]; then
wrapProgram "$file" \ wrapProgram "$file" \
--prefix PATH : "$deps" \ --prefix PATH : "$deps"
--set NOMARCHY_PATH "/etc/nixos/nomarchy"
fi fi
done done
''; '';

View File

@@ -2,4 +2,13 @@
{ {
environment.systemPackages = [ pkgs.nomarchy-system-scripts ]; environment.systemPackages = [ pkgs.nomarchy-system-scripts ];
# /etc/nixos is owned by root, but `nomarchy-env-update` (and `nix
# flake` invocations) run as the user and shell out to git. Without
# this, git refuses with "dubious ownership in repository" and HM
# evaluation fails. Mark both the standard and impermanence-relocated
# paths as safe at the system level so every user is covered.
programs.git.config = {
safe.directory = [ "/etc/nixos" "/persist/etc/nixos" ];
};
} }

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Returns the battery full capacity in Wh (rounded to whole number). # Returns the battery full capacity in Wh (rounded to whole number).
# Used by nomarchy-battery-status for displaying battery capacity. # Used by nomarchy-battery-status for displaying battery capacity.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Designed to be run by systemd timer every 30 seconds and alerts if battery is low # Designed to be run by systemd timer every 30 seconds and alerts if battery is low

View File

@@ -1,13 +0,0 @@
#!/bin/bash
# Returns true if a battery is present on the system.
# Used by the battery monitor and other battery-related checks.
for bat in /sys/class/power_supply/BAT*; do
[[ -r $bat/present ]] &&
[[ $(cat $bat/present) == "1" ]] &&
[[ $(cat $bat/type) == "Battery" ]] &&
exit 0
done
exit 1

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Returns the battery percentage remaining as an integer. # Returns the battery percentage remaining as an integer.
# Used by the battery monitor and the Ctrl + Shift + Super + B hotkey. # Used by the battery monitor and the Ctrl + Shift + Super + B hotkey.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Returns the battery time remaining (to empty or full) in a compact format. # Returns the battery time remaining (to empty or full) in a compact format.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Returns a formatted battery status string with percentage and power draw/charge. # Returns a formatted battery status string with percentage and power draw/charge.
# Used by the battery notification hotkey (Ctrl + Shift + Super + B). # Used by the battery notification hotkey (Ctrl + Shift + Super + B).

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Adjust brightness on the most likely display device. # Adjust brightness on the most likely display device.
# Usage: nomarchy-brightness-display <step> # Usage: nomarchy-brightness-display <step>

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Adjust the brightness on Apple Studio Displays and Apple XDR Displays using asdcontrol. # Adjust the brightness on Apple Studio Displays and Apple XDR Displays using asdcontrol.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Adjust keyboard backlight brightness using available steps. # Adjust keyboard backlight brightness using available steps.
# Usage: nomarchy-brightness-keyboard <up|down|cycle> # Usage: nomarchy-brightness-keyboard <up|down|cycle>

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Check if hibernation is supported # Check if hibernation is supported
if [[ ! -f /sys/power/image_size ]]; then if [[ ! -f /sys/power/image_size ]]; then

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Removes hibernation setup: disables swap, removes swapfile, removes fstab entry, # Removes hibernation setup: disables swap, removes swapfile, removes fstab entry,
# removes resume hook, and removes suspend-then-hibernate configuration. # removes resume hook, and removes suspend-then-hibernate configuration.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Creates a swap file in the btrfs subvolume, adds the swap file to /etc/fstab, # Creates a swap file in the btrfs subvolume, adds the swap file to /etc/fstab,
# adds a resume hook to mkinitcpio, and configures suspend-then-hibernate. # adds a resume hook to mkinitcpio, and configures suspend-then-hibernate.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Detect whether the computer is an Asus ROG machine. # Detect whether the computer is an Asus ROG machine.

View File

@@ -1,6 +0,0 @@
#!/bin/bash
# Detect whether the computer is a Framework Laptop 16.
[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "Framework" ]] &&
nomarchy-hw-match "Laptop 16"

View File

@@ -1,5 +0,0 @@
#!/bin/bash
# Detect whether the computer has an Intel CPU.
[[ $(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ') == "GenuineIntel" ]]

View File

@@ -1,5 +0,0 @@
#!/bin/bash
# Detect whether the computer has an Intel Panther Lake GPU.
lspci | grep -iE 'vga|3d|display' | grep -qi 'panther lake'

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Match against the computer's DMI product name (case-insensitive). # Match against the computer's DMI product name (case-insensitive).
# Usage: nomarchy-hw-match "XPS" # Usage: nomarchy-hw-match "XPS"

View File

@@ -1,6 +0,0 @@
#!/bin/bash
# Detect whether the computer is a Microsoft Surface device.
[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "Microsoft Corporation" ]] &&
nomarchy-hw-match "Surface"

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Detect whether Vulkan is available. # Detect whether Vulkan is available.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Returns a list of all the available power profiles on the system. # Returns a list of all the available power profiles on the system.
# Used by the Nomarchy Menu under Setup > Power Profile. # Used by the Nomarchy Menu under Setup > Power Profile.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Unblock and restart the bluetooth service. # Unblock and restart the bluetooth service.

View File

@@ -1,5 +0,0 @@
#!/bin/bash
# Restart makima - key remapping service for remapping Copilot key to Nomarchy Menu
sudo systemctl restart makima

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Restart the PipeWire audio service to fix audio issues or apply new configuration. # Restart the PipeWire audio service to fix audio issues or apply new configuration.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Reload the intel_quicki2c driver to fix a dead trackpad. # Reload the intel_quicki2c driver to fix a dead trackpad.
# The THC (Touch Host Controller) can fail to initialize interrupts # The THC (Touch Host Controller) can fail to initialize interrupts

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Unblock and restart the Wi-Fi service. # Unblock and restart the Wi-Fi service.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Restart the XCompose input method service (fcitx5) to apply new compose key settings. # Restart the XCompose input method service (fcitx5) to apply new compose key settings.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Configure DNS declaratively for Nomarchy NixOS. # Configure DNS declaratively for Nomarchy NixOS.
# Hybrid: updates /etc/nixos/state.json and runs sys-update. # Hybrid: updates /etc/nixos/state.json and runs sys-update.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Configure FIDO2 support declaratively for Nomarchy NixOS. # Configure FIDO2 support declaratively for Nomarchy NixOS.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Configure fingerprint support declaratively for Nomarchy NixOS. # Configure fingerprint support declaratively for Nomarchy NixOS.

View File

@@ -1,10 +0,0 @@
#!/bin/bash
# Prompt for sudo once and keep the credential alive in the background.
# Source this script so the trap applies to the calling shell:
# source nomarchy-sudo-keepalive
sudo -v
while true; do sudo -n true; sleep 60; done 2>/dev/null &
SUDO_KEEPALIVE_PID=$!
trap "kill $SUDO_KEEPALIVE_PID 2>/dev/null" EXIT

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Toggle passwordless sudo for the current user. # Toggle passwordless sudo for the current user.
# First run: enables passwordless sudo for 15 minutes (after confirmation). # First run: enables passwordless sudo for 15 minutes (after confirmation).

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Reset the sudo lockout/faillock for the current user. # Reset the sudo lockout/faillock for the current user.
# This clears any failed authentication attempts that may have locked the user out. # This clears any failed authentication attempts that may have locked the user out.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Display brightness level using SwayOSD on the current monitor. # Display brightness level using SwayOSD on the current monitor.
# Usage: nomarchy-swayosd-brightness <percent> # Usage: nomarchy-swayosd-brightness <percent>

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Display keyboard brightness level using SwayOSD on the current monitor. # Display keyboard brightness level using SwayOSD on the current monitor.
# Usage: nomarchy-swayosd-kbd-brightness <percent> # Usage: nomarchy-swayosd-kbd-brightness <percent>

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Logout command that first closes all application windows (thus giving them a chance to save state), # Logout command that first closes all application windows (thus giving them a chance to save state),
# then stops the session, returning to the SDDM login screen. # then stops the session, returning to the SDDM login screen.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Reboot command that first closes all application windows (thus giving them a chance to save state). # Reboot command that first closes all application windows (thus giving them a chance to save state).
# This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly. # This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Shutdown command that first closes all application windows (thus giving them a chance to save state). # Shutdown command that first closes all application windows (thus giving them a chance to save state).
# This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly. # This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14). # Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14).
# Declarative enablement + Runtime mode switching for Nomarchy NixOS. # Declarative enablement + Runtime mode switching for Nomarchy NixOS.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Toggles the idle daemon (hypridle) between enabled and disabled. # Toggles the idle daemon (hypridle) between enabled and disabled.
# Hybrid: updates state.json and provides instant feedback. # Hybrid: updates state.json and provides instant feedback.

View File

@@ -1,29 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Toggles the suspend menu option availability. # Execute system suspend
# Hybrid: updates state.json and runs env-update for persistence. systemctl suspend
STATE_DIR="$HOME/.config/nomarchy"
STATE_FILE="$STATE_DIR/state.json"
mkdir -p "$STATE_DIR"
# Initialize if doesn't exist
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
# Get current state from env or state file
if [[ $NOMARCHY_TOGGLE_SUSPEND == "false" ]]; then
NEW_VALUE="true"
notify-send -u low " Suspend now available in system menu"
else
NEW_VALUE="false"
notify-send -u low " Suspend removed from system menu"
fi
# Update JSON using jq with --argjson for proper boolean handling
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.suspend = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
echo "Suspend availability set to $NEW_VALUE. Updating environment..."
# Run nomarchy-env-update to apply changes to the menu
nomarchy-env-update

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Select system timezone declaratively for Nomarchy NixOS. # Select system timezone declaratively for Nomarchy NixOS.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
echo "Updating time..." echo "Updating time..."
sudo systemctl restart systemd-timesyncd sudo systemctl restart systemd-timesyncd

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# Toggles wifi power saving declaratively. # Toggles wifi power saving declaratively.
# Usage: nomarchy-wifi-powersave <on|off> # Usage: nomarchy-wifi-powersave <on|off>

42
core/system/snapper.nix Normal file
View File

@@ -0,0 +1,42 @@
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system.snapper;
rootIsBtrfs = (config.fileSystems."/".fsType or "") == "btrfs";
active = cfg.enable && rootIsBtrfs;
in
{
config = lib.mkIf active {
# `nixos-rebuild-snap`: take a Snapper pre-rebuild snapshot, then run
# `nixos-rebuild switch` against the current host. The hostname is read
# from the running config so this script works on every machine without
# editing.
environment.systemPackages = [
(pkgs.writeShellScriptBin "nixos-rebuild-snap" ''
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root (use sudo)" >&2
exit 1
fi
echo "Creating pre-rebuild snapshot..."
${pkgs.snapper}/bin/snapper -c root create \
-d "Pre-rebuild $(date +'%Y-%m-%d %H:%M:%S')" \
--cleanup-algorithm number
echo "Rebuilding..."
nixos-rebuild switch --flake .#${config.networking.hostName} "$@"
'')
];
services.snapper.configs = {
root = {
SUBVOLUME = "/";
TIMELINE_CREATE = true;
TIMELINE_CLEANUP = true;
TIMELINE_LIMIT_HOURLY = "5";
TIMELINE_LIMIT_DAILY = "7";
TIMELINE_LIMIT_WEEKLY = "0";
TIMELINE_LIMIT_MONTHLY = "0";
TIMELINE_LIMIT_YEARLY = "0";
};
};
};
}

View File

@@ -2,20 +2,28 @@
let let
nomarchyLib = import ../../lib { inherit lib; }; nomarchyLib = import ../../lib { inherit lib; };
# Same canonical schema as core/home/state.nix and the options.nix
# files — keeps every state default in one place.
schema = import ../../lib/state-schema.nix { inherit lib; };
systemState = nomarchyLib.readSystemState; systemState = nomarchyLib.readSystemState;
in 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.
config.nomarchy.system = { config.nomarchy.system = {
dns = systemState.dns or "DHCP"; dns = lib.mkDefault (systemState.dns or schema.system.dns);
customDns = systemState.customDns or []; customDns = lib.mkDefault (systemState.customDns or schema.system.customDns);
wifi.powersave = systemState.wifi.powersave or true; wifi.powersave = lib.mkDefault (systemState.wifi.powersave or schema.system.wifi.powersave);
timezone = systemState.timezone or "UTC"; timezone = lib.mkDefault (systemState.timezone or schema.system.timezone);
features = { features = {
fingerprint = systemState.features.fingerprint or false; fingerprint = lib.mkDefault (systemState.features.fingerprint or schema.system.features.fingerprint);
fido2 = systemState.features.fido2 or false; fido2 = lib.mkDefault (systemState.features.fido2 or schema.system.features.fido2);
hybridGPU = systemState.features.hybridGPU or false; hybridGPU = lib.mkDefault (systemState.features.hybridGPU or schema.system.features.hybridGPU);
makima = systemState.features.makima or false;
}; };
theme = systemState.theme or "nord"; theme = lib.mkDefault (systemState.theme or schema.system.theme);
}; };
} }

View File

@@ -1,6 +1,12 @@
{ lib, ... }: { config, lib, pkgs, ... }:
let
libvirt = config.nomarchy.system.virtualization.libvirt.enable;
docker = config.nomarchy.system.virtualization.docker.enable;
in
{ {
# uwsm + Hyprland session — present on every Nomarchy install regardless
# of the optional libvirt branch below.
programs.uwsm = { programs.uwsm = {
enable = lib.mkDefault true; enable = lib.mkDefault true;
waylandCompositors.hyprland = { waylandCompositors.hyprland = {
@@ -8,4 +14,23 @@
prettyName = "Hyprland"; prettyName = "Hyprland";
}; };
}; };
# Optional: libvirt + virt-manager + OVMF. Toggle with
# `nomarchy.system.virtualization.libvirt.enable = true;`. The user must
# be in the `libvirtd` group to drive virsh / virt-manager.
virtualisation.libvirtd.enable = lib.mkIf libvirt true;
# Optional: Docker + docker-compose.
virtualisation.docker.enable = lib.mkIf docker true;
environment.systemPackages = lib.mkMerge [
(lib.mkIf libvirt (with pkgs; [
virt-manager
qemu
OVMF
]))
(lib.mkIf docker (with pkgs; [
docker-compose
]))
];
} }

247
docs/AGENT.md Normal file
View File

@@ -0,0 +1,247 @@
# Agent Instructions for Nomarchy
You're an AI coding agent picking up the Nomarchy project. This document gives you everything you need to be useful from the first turn: what Nomarchy is, how it's organized, what the rules are, and how to actually pick up the next piece of work.
If anything below conflicts with what the user just said, the user wins. If anything below conflicts with the existing code, read the code — these notes can rot.
---
## 1. What Nomarchy is
Nomarchy is a NixOS-based distribution that ships a highly curated Hyprland desktop on a strictly declarative, flake-based foundation. It targets power users who want a polished, reproducible desktop environment.
Concretely:
- A flake at the repo root exposes `nixosModules.system` (foundational OS modules) and `nixosModules.home` (apps + desktop), plus three `nixosConfigurations` (`nomarchy-installer`, `nomarchy-live`, `default`) and standalone `homeConfigurations`.
- Downstream users get the distro by importing `nomarchy.nixosModules.system` and `nomarchy.nixosModules.home` from their own `/etc/nixos/flake.nix`. The Nomarchy installer generates that flake for them; an existing-NixOS user can hand-write it (see `docs/MIGRATION.md`).
- A bash/`gum` TUI installer lives in `installer/install.sh` (~1100 lines). It auto-detects hardware via `installer/hardware-db.sh` (DMI + `lspci` + `BAT*` sysfs), prompts for the rest, and generates `flake.nix`, `system.nix`, `home.nix`, and `hardware-selection.nix` into `/mnt/etc/nixos/`.
- The desktop is Hyprland + waybar + walker + a curated theming engine (`themes/`) with 22 palettes wired through Stylix.
Read in this order to come up to speed:
1. `README.md` — public face.
2. `docs/STRUCTURE.md` — directory layout and module logic.
3. `docs/OPTIONS.md` — every `nomarchy.*` option a downstream flake can set.
4. `docs/ROADMAP.md` — what's planned. **This is your work queue.**
5. `docs/SCRIPTS.md` — the script & menu audit table (Pillar 3 of the roadmap).
6. `docs/MIGRATION.md` — how an existing NixOS install becomes Nomarchy.
7. `docs/creating-themes.md` — when palette work comes up.
---
## 2. How the repo is organized
```
core/ Foundational OS + user defaults. Don't put apps here.
system/ NixOS modules, all imported via core/default.nix.
options.nix nomarchy.system.* options live here.
hardware.nix nomarchy.hardware.* options + module wiring.
network.nix NetworkManager, DNS, networkmanagerapplet.
impermanence.nix Erase-Your-Darlings root wipe.
scripts/ Low-level system scripts (battery, brightness, hardware).
home/ Home Manager modules.
options.nix Most home-side nomarchy.* options.
overrides.nix nomarchy.overrides.* (reserved; currently no-op — see ROADMAP).
config/ Plain dotfiles symlinked into ~/.config.
features/ Apps and desktop components. Add new apps here.
apps/ One subdir per app (alacritty, btop, kitty, vscode.nix, …).
desktop/ hyprland, waybar, idle.nix, nightlight.nix, …
scripts/ User-PATH scripts, battery-monitor user service.
utils/ ~68 nomarchy-* user scripts.
default.nix Packages them as nomarchy-system-scripts derivation.
themes/ Theme engine + 22 palettes.
engine/ Loader, Stylix glue, switcher, scripts (font, theme, wallpaper).
palettes/ One subdir per palette (summer-night, tokyo-night, …).
hosts/ ISO host configs (nomarchy-installer, nomarchy-live/live-iso).
installer/ The bash/gum TUI + disko configs + hardware-db.sh.
lib/ Shared Nix helpers (state schema, color resolution, paths).
docs/ All long-form documentation. README.md stays at repo root.
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 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`.
---
## 3. Guardrails (non-negotiable unless the user overrides)
These are inherited from the established Nomarchy conventions. Violating them is a bug.
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.
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.
8. **Docs ride with the change.** Every change ships in the same commit as its doc updates — no follow-up "docs catch-up" PRs. The mapping is in §5.4 below; if a change touches something in that table and the matching doc didn't move with it, the change is incomplete.
---
## 4. How to find work to do
The roadmap (`docs/ROADMAP.md`) is the source of truth. It has three columns and seven pillars.
**Default rule:** prefer items in the **Now** column. Pick whichever matches the user's current ask, or the smallest one if no ask is in flight.
If the user asked for something not in the roadmap:
- Do the work.
- After it ships, propose a one-line addition to `docs/ROADMAP.md` so future you doesn't re-discover it.
If the user asks "what's next?", reply with the Now column items in 23 sentences and let them pick.
### The script & menu audit (Pillar 3)
This is the **largest single open work item**. Phase A (inventory) hasn't run yet; `docs/SCRIPTS.md` is just scaffolding. The user explicitly flagged this pillar as important.
Phase A shipped. The inventory lives at [`docs/SCRIPTS.md`](SCRIPTS.md) and is regenerated by:
```bash
./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md
```
It tags every row with one of `kept` / `unused?` / `missing`. Phase B is the per-batch porting/removal: pick ~10 rows tagged `missing` or `unused?`, decide one of `port-from-omarchy` / `delete-dead` / `stub-with-notify`, and ship as one PR per batch on a `wave/audit-<batch>` branch. Each PR description should reference the rows it closes; reviewers spot-check that every caller of a removed/renamed script is updated.
---
## 5. Workflow per change
Steps you should follow for any non-trivial change:
1. **Understand the current state first.**
- `git status` and `git log -5` so you know what's just landed and what's in flight.
- Read the relevant files. Don't infer.
2. **Plan if it's non-trivial.** Tasks with three or more steps benefit from a TaskCreate list. Tasks that touch the architecture benefit from plan mode.
3. **Reuse existing options and scripts.** Grep `core/system/options.nix`, `core/home/options.nix`, and the three script directories before adding anything.
4. **Touch the docs in the same change.** Use this table — if your change matches a row, the doc edit ships in the same commit. No exceptions, no follow-ups.
| If you change… | Update in the same commit |
| --- | --- |
| A `nomarchy.*` option (added, removed, renamed, default flipped, type changed, description changed) | `docs/OPTIONS.md` (matching row) **and** any other row that mentions it (e.g. `formFactor` is referenced from `laptop.enable`'s row) |
| A `nomarchy-*` script (added, removed, renamed) | `docs/SCRIPTS.md` — the pre-commit hook regenerates it; verify it's staged. If `core.hooksPath` isn't set, run `./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md` manually. |
| A keybinding (`bindd =` / `bindeld =`) under `core/home/config/.../hypr/` | `docs/KEYBINDINGS.md` — regenerate via `./bin/utils/nomarchy-docs-keybindings --out docs/KEYBINDINGS.md`; spot-check the README highlights still hold |
| A directory was added/removed/renamed under `core/`, `features/`, `themes/`, `hosts/`, `installer/`, `lib/`, `bin/`, or `docs/` | `docs/STRUCTURE.md` (tree + module logic prose) **and** §2 of this file (`docs/AGENT.md`) if the change is structural enough to affect new-agent onboarding |
| A feature module's behavior changed in a way an existing user would notice (a default flipped, a service started/stopped, a key remapped) | `README.md` if it's user-visible at the marketing level; otherwise just `docs/OPTIONS.md` |
| An installer prompt added/removed/reordered, or generated-file shape changed | `docs/MIGRATION.md` if it affects hand-written `flake.nix` setups; `installer/install.sh` heredoc comments if a generated file's shape changed |
| A theme palette added/removed, or the theming engine's contract changed | `docs/creating-themes.md` and the palette count in `README.md` |
| A roadmap item shipped | Move the entry to the **Shipped** section at the bottom of `docs/ROADMAP.md`. Don't delete — that's our changelog. |
| A roadmap item discovered (new scope) | Add a one-line row to **Now**, **Next**, or **Later** in `docs/ROADMAP.md`. Don't tell the user verbally and forget. |
| A workflow / convention / guardrail you'd want a future agent to follow | `docs/AGENT.md` (this file). |
| A new flake input, output, or major architectural decision | `docs/STRUCTURE.md` and `docs/AGENT.md` §1. |
Before declaring done, do one explicit pass: did the change cross any row above? If yes, is the matching doc edit staged? If you can't answer "yes" to both, you're not done.
5. **Verify the change evaluates** (cheap, do this before declaring done):
```bash
nix --extra-experimental-features 'nix-command flakes' flake check --no-build
bash -n installer/install.sh # if you touched it
```
First clone? Enable the repo's pre-commit hook so `docs/SCRIPTS.md` regenerates whenever you add/modify/remove a `nomarchy-*` script:
```bash
git config core.hooksPath .githooks
```
For installer changes, also do a dry-run end-to-end:
```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.
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.
---
## 6. Patterns worth knowing
### Adding a new option
```nix
# core/system/options.nix (or core/home/options.nix for home-side)
mything.enable = lib.mkEnableOption ''
Concise description that reads well in `nix eval`. Mention any
pre-conditions (groups, kernel modules, hardware needed).
'';
```
Then wire it inside a `config = lib.mkIf cfg.mything.enable { ... }` block and document it in `docs/OPTIONS.md`.
### Filtering modules conditionally
We do this for waybar (drop `battery` on desktop) in `features/desktop/waybar/default.nix:25-39`. Pattern:
```nix
let
rawSettings = builtins.fromJSON (builtins.readFile configFile);
laptopOnly = [ "battery" "custom/battery" ];
filterModules = mods:
if config.nomarchy.formFactor == "laptop" then mods
else builtins.filter (m: !(builtins.elem m laptopOnly)) mods;
settings = rawSettings // {
modules-right = filterModules (rawSettings.modules-right or []);
modules-center = filterModules (rawSettings.modules-center or []);
modules-left = filterModules (rawSettings.modules-left or []);
};
in { ... }
```
### Form-factor (laptop vs desktop)
`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`)
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.
### Scripts derivation
User-PATH scripts ship via `nomarchy-system-scripts` (`core/system/scripts-derivation.nix`) plus the per-category dependencies declared in `features/scripts/default.nix:categoryDeps`. When you add a script:
1. Drop the file in `features/scripts/utils/` or `core/system/scripts/`.
2. `chmod +x` it (it'll be wrapped with the right deps automatically).
3. Reference the right `categoryDeps` group in `features/scripts/default.nix` if it needs new tools.
4. Test that `which <script>` resolves after a rebuild.
### Heredocs in `installer/install.sh`
The system-config generator uses unquoted heredocs (`<< EOF`) so `$VAR` expands. The home-config generator now also uses unquoted heredocs — any literal `$` or backtick in the body must be escaped (see `installer/install.sh:1043-1131`). If you forget and the install fails with a "unbound variable" or unexpected command output, that's why.
---
## 7. Things to never do without explicit user OK
- `git push --force`, `git reset --hard`, `git clean -f`, `rm -rf` outside obvious sandbox dirs.
- Touch `flake.lock` unless the user asked to update inputs.
- Bump `nixpkgs` major version (e.g. `25.11` → `26.05`) — that's a release decision.
- Add a new flake input. They're load-bearing across all three host configurations.
- Skip pre-commit hooks (`--no-verify`).
- Ship a feature without a `nomarchy.*` option to gate it.
- Ship a removal without updating callers (grep first).
- Mock the database / external services in tests — use the real thing.
---
## 8. Where to put your own notes
- **Long-lived docs** → `docs/`.
- **Working notes / per-session plans** → `~/.claude/plans/`. Don't commit them.
- **Memory** about the user, this project, or process preferences → your harness's memory directory at `/home/bernardo/.claude/projects/-home-bernardo-Projects-nomarchy/memory/`. Each entry gets a frontmatter file plus a one-line index entry in `MEMORY.md`.
---
## 9. The shape of a good handoff
Before you stop, leave the next agent (or future you) something to land on:
- The roadmap is up to date — items you shipped are in **Shipped**.
- `git status` is clean (or has one obvious WIP commit on a `wave/...` branch).
- If you discovered new scope, you logged it as a roadmap row, not a verbal heads-up.
- Any plan file you wrote in `~/.claude/plans/` is named after the task, not the date.
- The user's last message has been answered concisely.
That's it. The roadmap tells you what; this file tells you how. Go pick a Now item.

218
docs/KEYBINDINGS.md Normal file
View File

@@ -0,0 +1,218 @@
# 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.
## Utilities
_Source: `core/home/config/nomarchy/default/hypr/bindings/utilities.conf`_
| Modifiers | Key | Action |
| --- | --- | --- |
| SUPER | SPACE | Launch apps |
| SUPER CTRL | E | Emoji picker |
| SUPER CTRL | C | Capture menu |
| SUPER CTRL | O | Toggle menu |
| SUPER ALT | SPACE | Toggle top bar |
| SUPER | ESCAPE | System menu |
| — | XF86PowerOff | Power menu |
| SUPER | K | Show key bindings |
| — | XF86Calculator | Calculator |
| SUPER SHIFT | SPACE | Nomarchy menu |
| SUPER CTRL | SPACE | Theme background menu |
| SUPER SHIFT CTRL | SPACE | Theme menu |
| SUPER | BACKSPACE | Toggle window transparency |
| SUPER SHIFT | BACKSPACE | Toggle window gaps |
| SUPER CTRL | BACKSPACE | Toggle single-window square aspect |
| SUPER | COMMA | Dismiss last notification |
| SUPER SHIFT | COMMA | Dismiss all notifications |
| SUPER CTRL | COMMA | Toggle silencing notifications |
| SUPER ALT | COMMA | Invoke last notification |
| SUPER SHIFT ALT | COMMA | Restore last notification |
| SUPER CTRL | I | Toggle locking on idle |
| SUPER CTRL | N | Toggle nightlight |
| CTRL | F1 | Apple Display brightness down |
| CTRL | F2 | Apple Display brightness up |
| SHIFT CTRL | F2 | Apple Display full brightness |
| — | PRINT | Screenshot |
| ALT | PRINT | Screenrecording |
| SUPER | PRINT | Color picker |
| SUPER CTRL | S | Share |
| SUPER CTRL ALT | T | Show time |
| SUPER CTRL ALT | B | Show battery remaining |
| SUPER CTRL | A | Audio controls |
| 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 |
## Tiling
_Source: `core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf`_
| Modifiers | Key | Action |
| --- | --- | --- |
| SUPER | W | Close window |
| CTRL ALT | DELETE | Close all windows |
| SUPER | J | Toggle window split |
| SUPER | P | Pseudo window |
| SUPER | T | Toggle window floating/tiling |
| SUPER | F | Full screen |
| SUPER CTRL | F | Tiled full screen |
| SUPER ALT | F | Full width |
| SUPER | O | Pop window out (float & pin) |
| SUPER | L | Toggle workspace layout |
| SUPER | LEFT | Move window focus left |
| SUPER | RIGHT | Move window focus right |
| SUPER | UP | Move window focus up |
| SUPER | DOWN | Move window focus down |
| SUPER | 1 | Switch to workspace 1 |
| SUPER | 2 | Switch to workspace 2 |
| SUPER | 3 | Switch to workspace 3 |
| SUPER | 4 | Switch to workspace 4 |
| SUPER | 5 | Switch to workspace 5 |
| SUPER | 6 | Switch to workspace 6 |
| SUPER | 7 | Switch to workspace 7 |
| SUPER | 8 | Switch to workspace 8 |
| SUPER | 9 | Switch to workspace 9 |
| SUPER | 0 | Switch to workspace 10 |
| SUPER SHIFT | 1 | Move window to workspace 1 |
| SUPER SHIFT | 2 | Move window to workspace 2 |
| SUPER SHIFT | 3 | Move window to workspace 3 |
| SUPER SHIFT | 4 | Move window to workspace 4 |
| SUPER SHIFT | 5 | Move window to workspace 5 |
| SUPER SHIFT | 6 | Move window to workspace 6 |
| SUPER SHIFT | 7 | Move window to workspace 7 |
| SUPER SHIFT | 8 | Move window to workspace 8 |
| SUPER SHIFT | 9 | Move window to workspace 9 |
| SUPER SHIFT | 0 | Move window to workspace 10 |
| SUPER SHIFT ALT | 1 | Move window silently to workspace 1 |
| SUPER SHIFT ALT | 2 | Move window silently to workspace 2 |
| SUPER SHIFT ALT | 3 | Move window silently to workspace 3 |
| SUPER SHIFT ALT | 4 | Move window silently to workspace 4 |
| SUPER SHIFT ALT | 5 | Move window silently to workspace 5 |
| SUPER SHIFT ALT | 6 | Move window silently to workspace 6 |
| SUPER SHIFT ALT | 7 | Move window silently to workspace 7 |
| SUPER SHIFT ALT | 8 | Move window silently to workspace 8 |
| SUPER SHIFT ALT | 9 | Move window silently to workspace 9 |
| SUPER SHIFT ALT | 0 | Move window silently to workspace 10 |
| SUPER | S | Toggle scratchpad |
| SUPER ALT | S | Move window to scratchpad |
| SUPER | TAB | Next workspace |
| SUPER SHIFT | TAB | Previous workspace |
| SUPER CTRL | TAB | Former workspace |
| SUPER SHIFT ALT | LEFT | Move workspace to left monitor |
| SUPER SHIFT ALT | RIGHT | Move workspace to right monitor |
| SUPER SHIFT ALT | UP | Move workspace to up monitor |
| SUPER SHIFT ALT | DOWN | Move workspace to down monitor |
| SUPER SHIFT | LEFT | Swap window to the left |
| SUPER SHIFT | RIGHT | Swap window to the right |
| SUPER SHIFT | UP | Swap window up |
| SUPER SHIFT | DOWN | Swap window down |
| ALT | TAB | Cycle to next window |
| ALT SHIFT | TAB | Cycle to prev window |
| ALT | TAB | Reveal active window on top |
| ALT SHIFT | TAB | Reveal active window on top |
| SUPER | code:20 | Expand window left |
| SUPER | code:21 | Shrink window left |
| SUPER SHIFT | code:20 | Shrink window up |
| SUPER SHIFT | code:21 | Expand window down |
| SUPER | mouse_down | Scroll active workspace forward |
| SUPER | mouse_up | Scroll active workspace backward |
| SUPER | mouse:272 | Move window |
| SUPER | mouse:273 | Resize window |
| SUPER | G | Toggle window grouping |
| SUPER ALT | G | Move active window out of group |
| SUPER ALT | LEFT | Move window to group on left |
| SUPER ALT | RIGHT | Move window to group on right |
| SUPER ALT | UP | Move window to group on top |
| SUPER ALT | DOWN | Move window to group on bottom |
| SUPER ALT | TAB | Next window in group |
| SUPER ALT SHIFT | TAB | Previous window in group |
| SUPER CTRL | LEFT | Move grouped window focus left |
| SUPER CTRL | RIGHT | Move grouped window focus right |
| SUPER ALT | mouse_down | Next window in group |
| SUPER ALT | mouse_up | Previous window in group |
| SUPER ALT | 1 | Switch to group window 1 |
| SUPER ALT | 2 | Switch to group window 2 |
| SUPER ALT | 3 | Switch to group window 3 |
| SUPER ALT | 4 | Switch to group window 4 |
| SUPER ALT | 5 | Switch to group window 5 |
| SUPER | Slash | Cycle monitor scaling |
## Clipboard
_Source: `core/home/config/nomarchy/default/hypr/bindings/clipboard.conf`_
| Modifiers | Key | Action |
| --- | --- | --- |
| SUPER | C | Universal copy |
| SUPER | V | Universal paste |
| SUPER | X | Universal cut |
| SUPER CTRL | V | Clipboard manager |
## Media keys
_Source: `core/home/config/nomarchy/default/hypr/bindings/media.conf`_
| Modifiers | Key | Action |
| --- | --- | --- |
| — | Volume Up | Volume up |
| — | Volume Down | Volume down |
| — | Mute | Mute |
| — | Mic Mute | Mute microphone |
| — | Brightness Up | Brightness up |
| — | Brightness Down | Brightness down |
| — | Kbd Brightness Up | Keyboard brightness up |
| — | Kbd Brightness Down | Keyboard brightness down |
| — | Kbd Backlight | Keyboard backlight cycle |
| ALT | Volume Up | Volume up precise |
| ALT | Volume Down | Volume down precise |
| ALT | Brightness Up | Brightness up precise |
| ALT | Brightness Down | Brightness down precise |
| — | Next Track | Next track |
| — | XF86AudioPause | Pause |
| — | Play/Pause | Play |
| — | Previous Track | Previous track |
| SUPER | Mute | Switch audio output |
## Apps & web shortcuts
_Source: `features/desktop/hyprland/config/bindings.conf`_
| Modifiers | Key | Action |
| --- | --- | --- |
| SUPER | RETURN | Terminal |
| SUPER ALT | RETURN | Tmux |
| SUPER SHIFT | RETURN | Browser |
| SUPER SHIFT | F | File manager |
| SUPER ALT SHIFT | F | File manager (cwd) |
| SUPER SHIFT | B | Browser |
| SUPER SHIFT ALT | B | Browser (private) |
| SUPER SHIFT | M | Music |
| SUPER SHIFT | N | Editor |
| SUPER SHIFT | D | Docker |
| SUPER SHIFT | G | Signal |
| SUPER SHIFT | O | Obsidian |
| SUPER SHIFT | SLASH | Passwords |
| SUPER SHIFT | A | ChatGPT |
| SUPER SHIFT ALT | A | Grok |
| SUPER SHIFT | C | Calendar |
| SUPER SHIFT | E | Email |
| SUPER SHIFT | Y | YouTube |
| SUPER SHIFT ALT | G | WhatsApp |
| SUPER SHIFT CTRL | G | Google Messages |
| SUPER SHIFT | P | Google Photos |
| SUPER SHIFT | X | X |
| SUPER SHIFT ALT | X | X Post |

259
docs/MIGRATION.md Normal file
View File

@@ -0,0 +1,259 @@
# Migrating an existing NixOS install to Nomarchy
You already have NixOS running and you want the Nomarchy desktop without
reformatting. This is the in-place path. Your `/`, `/home`, hostname, and
user account are preserved; Nomarchy is layered on as a flake input.
> Prefer a clean disk? Skip to the [fallback](#fallback-clean-install-via-the-live-iso).
---
## Prerequisites
- NixOS 25.11. Older releases are not supported by the current Nomarchy
modules.
- Flakes enabled. If you don't have them yet:
```nix
# somewhere in your existing config
nix.settings.experimental-features = [ "nix-command" "flakes" ];
```
Apply with `sudo nixos-rebuild switch` once before proceeding.
---
## 1. Move to a flake-based config
If you already have `/etc/nixos/flake.nix`, skip to step 2.
Back your existing config up:
```bash
sudo cp /etc/nixos/configuration.nix /etc/nixos/configuration.nix.bak
```
Then create `/etc/nixos/flake.nix` with the structure below. (This is the
same shape the Nomarchy installer produces — see
`installer/install.sh:generate_flake_config` in the upstream repo.)
```nix
{
description = "My Nomarchy Configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nomarchy.url = "git+https://git.bemagri.xyz/bernardo/Nomarchy.git";
# …or pin: "git+https://git.bemagri.xyz/bernardo/Nomarchy.git?rev=<sha>"
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, nomarchy, home-manager, nixos-hardware, ... }@inputs:
let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
overlays = [ nomarchy.overlays.default ];
config.allowUnfree = true;
};
in
{
nixosConfigurations.<hostname> = nixpkgs.lib.nixosSystem {
inherit pkgs;
specialArgs = { inputs = nomarchy.inputs // inputs; };
modules = [
./hardware-configuration.nix
nomarchy.nixosModules.system
./system.nix
];
};
homeConfigurations.<user> = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
extraSpecialArgs = { inputs = nomarchy.inputs // inputs; };
modules = [
nomarchy.nixosModules.home
./home.nix
{
home.username = "<user>";
home.homeDirectory = "/home/<user>";
home.stateVersion = "25.11";
}
];
};
};
}
```
Replace `<hostname>` and `<user>` with your real values. Two-track Nomarchy:
the system block uses `nomarchy.nixosModules.system`; the standalone
home-manager block uses `nomarchy.nixosModules.home`. Both consume the same
`pkgs` so overlays stay in sync.
## 2. Move your existing settings into `system.nix` and `home.nix`
The Nomarchy installer also generates these files — copy that template, or
create minimal versions to start:
`/etc/nixos/system.nix`:
```nix
{ pkgs, ... }:
{
networking.hostName = "<hostname>";
time.timeZone = "<your-timezone>";
users.users."<user>" = {
isNormalUser = true;
extraGroups = [ "wheel" "video" "render" "audio" "networkmanager" ];
};
# Compressed RAM swap — near-free memory headroom.
zramSwap.enable = true;
system.stateVersion = "25.11";
}
```
`/etc/nixos/home.nix`:
```nix
{ pkgs, ... }:
{
home.packages = with pkgs; [
btop
fastfetch
chromium
# …add anything you want; firefox/thunar/mpv/mako/etc. ship with Nomarchy.
];
}
```
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
them — see the [conflicts](#conflicts-to-resolve-before-rebuild) section.
## 3. (Optional) Pick up hardware-specific tuning
Nomarchy ships a `nixos-hardware` matcher. From any shell on your existing
system:
```bash
nix shell nixpkgs#git -c \
bash -c 'tmp=$(mktemp -d); git clone --depth=1 https://git.bemagri.xyz/bernardo/Nomarchy.git "$tmp" >/dev/null 2>&1; \
source "$tmp/installer/hardware-db.sh"; nomarchy_detect_hw'
```
Output is a list like:
```
MODULE common-cpu-amd
MODULE common-gpu-amd
MODULE common-pc-laptop
MODULE framework-13-amd-ai-300-series
OPT isFramework=true
```
Drop a `hardware-selection.nix` next to your flake:
```nix
{ inputs, ... }:
{
imports = [
inputs.nixos-hardware.nixosModules.common-cpu-amd
inputs.nixos-hardware.nixosModules.common-gpu-amd
inputs.nixos-hardware.nixosModules.framework-13-amd-ai-300-series
];
nomarchy.hardware.isFramework = true;
}
```
…and add `./hardware-selection.nix` to the system module list in `flake.nix`.
Skip this entirely if you don't have a matching device.
## 4. Rebuild
```bash
sudo nixos-rebuild switch --flake /etc/nixos#<hostname>
home-manager switch --flake /etc/nixos#<user> --impure
```
Reboot or `systemctl restart display-manager`. SDDM comes up with the
Nomarchy theme; logging in as `<user>` lands you in Hyprland with the full
Nomarchy desktop.
After this, the daily workflow is two commands:
- **System changes** (services, kernel, hardware) →
`sudo nixos-rebuild switch --flake /etc/nixos#<hostname>`
- **Dotfiles, themes, user packages** → `nomarchy-env-update` (runs the
standalone `home-manager switch` for you, no system rebuild)
---
## Conflicts to resolve before rebuild
`nomarchy.nixosModules.system` enables a desktop stack. If your existing
`configuration.nix` already configures any of these, only one setting wins
and it's whichever has higher Nix priority. Fix these explicitly:
| Area | Nomarchy default | What to do if you already have it |
| --- | --- | --- |
| Display manager | SDDM (Wayland) | Disable yours: `services.xserver.displayManager.gdm.enable = lib.mkForce false;` (or whatever you had) |
| Default session | `hyprland-uwsm` | Drop your `services.displayManager.defaultSession` |
| Hyprland | `programs.hyprland.enable = true; withUWSM = true;` | Drop your `programs.hyprland` setting |
| Audio | PipeWire (alsa+pulse+jack) | Remove `services.pulseaudio.enable = true;` — NixOS errors if both are on |
| Networking | NetworkManager | Drop `networking.wireless.enable = true;` (if set) |
| Graphics | `hardware.graphics.enable = true` (was `hardware.opengl`) | Probably already enabled — fine |
| User groups | needs `video render networkmanager` | Add to your `users.users.<user>.extraGroups` |
| `/etc/os-release` | `ID=nomarchy`, `NAME=Nomarchy` | A few third-party scripts grep `ID=nixos` — adjust them or rely on `ID_LIKE` (TBD) |
| autoLogin | `enable = false; user = "nomarchy";` (mkDefault) | Off by default — opt in with `services.displayManager.autoLogin = { enable = true; user = "<your user>"; };` if you want it |
Impermanence is **off** unless you set `nomarchy.system.impermanence.enable = true`,
and it requires a BTRFS layout with a `root-blank` snapshot. Don't enable it
on an existing install — the live ISO is the right path for that.
If your first rebuild errors out, the five most common failures and their fixes
live in [Troubleshooting](TROUBLESHOOTING.md).
---
## Fallback: clean install via the live ISO
```bash
# In a checkout of the Nomarchy repo:
nomarchy-test-live-iso # boots the ISO in QEMU to evaluate
# Or burn the produced .iso to a USB and boot it on real hardware.
```
The ISO autologins to a Hyprland live session that points you at:
- `sudo /etc/install.sh` — install (BTRFS + LUKS + subvolumes per
`installer/disko-config.nix`, auto-detects hardware via `hardware-db.sh`,
runs `home-manager switch` inside `nixos-enter` so the first login is
fully themed).
- `sudo /etc/install.sh --dry-run` — generate the flake into a tmpdir and
parse-check it without touching the disk.
- `sudo /etc/install.sh --resume` — pick up an interrupted run.
After install, the system at `/etc/nixos/` is the same shape this guide
produces by hand.
---
## Verification (in-place migration)
1. `cat /etc/os-release` → `NAME=Nomarchy`, `ID=nomarchy`.
2. SDDM theme is the Nomarchy purple/blue panel.
3. After login: waybar shows the Nomarchy logo on the left, theme switcher
under Style → Theme works.
4. `which btop fastfetch waybar walker nomarchy-env-update` → all on PATH.
5. `nomarchy-env-update` returns clean (proves standalone HM is wired and
`nomarchy.nixosModules.home` is in scope).
If anything is wrong, your old config is intact at
`/etc/nixos/configuration.nix.bak` — `sudo nixos-rebuild switch -I nixos-config=/etc/nixos/configuration.nix.bak`
will roll back.

328
docs/OPTIONS.md Normal file
View File

@@ -0,0 +1,328 @@
# Nomarchy Options Reference
Every option Nomarchy exposes for downstream flakes. Paths under `nomarchy.system.*` are NixOS options (set in `system.nix`); paths under `nomarchy.*` (no `system` segment) are Home Manager options (set in `home.nix`). `nomarchy.hardware.*` is NixOS.
The installer-generated configuration writes a few of these for you (timezone, formFactor, hardware vendor flags, keymap, locale). Anything not listed there is opt-in — set it yourself.
To see the live default for any option:
```bash
nix eval .#nixosConfigurations.<host>.config.nomarchy.system.<path>
nix eval .#homeConfigurations.<user>.config.nomarchy.<path>
```
---
## NixOS options (`system.nix`)
### `nomarchy.system.dns`
DNS provider. One of `"DHCP"` (default), `"Cloudflare"`, `"Google"`, `"Custom"`. With `"Custom"`, set `nomarchy.system.customDns` to a list of nameservers. Anything other than `"DHCP"` also enables `services.resolved` with DNSSEC and DNS-over-TLS.
Defined in `core/system/options.nix`; wired in `core/system/network.nix`.
### `nomarchy.system.customDns`
`listOf str`, default `[]`. Nameservers used when `dns = "Custom"`.
### `nomarchy.system.wifi.powersave`
`bool`, default `true`. Sets `networking.networkmanager.wifi.powersave`. Turn off if you see drops on idle Wi-Fi.
### `nomarchy.system.timezone`
`str`, default `"UTC"`. The installer writes `time.timeZone` directly, so this option is informational unless you wire it into your own modules.
### `nomarchy.system.formFactor`
`enum [ "laptop" "desktop" ]`, default `"laptop"`. Drives UI affordances and the laptop power preset. The installer auto-detects via `/sys/class/power_supply/BAT*`. The default is `"laptop"` because the battery widget renders empty when no battery is present — safe on a desktop, useful on a laptop.
Wired in `features/desktop/waybar/default.nix` (filters the battery widget out on desktop), `features/scripts/battery-monitor.nix` (skips the timer on desktop), and `nomarchy.system.laptop.enable` (defaults true when this is `"laptop"`).
### `nomarchy.system.theme`
`str`, default `"summer-night"`. Theme name. Mirror of the home-side `nomarchy.theme`. Set both if you also want NixOS-side modules (e.g. SDDM theming) to follow.
### `nomarchy.system.features.fingerprint`
`bool`, default `false`. Enables `services.fprintd.enable`.
### `nomarchy.system.features.fido2`
`bool`, default `false`. Enables `security.pam.u2f` (sufficient, with cue).
### `nomarchy.system.features.hybridGPU`
`bool`, default `false`. NVIDIA-hybrid laptop support. Wires:
- `services.supergfxd.enable` for runtime mode switching (`Integrated` / `Hybrid` / `Vfio` / `AsusEgpu`), driven by `nomarchy-toggle-hybrid-gpu`.
- The NVIDIA driver stack (`services.xserver.videoDrivers = ["nvidia"]`, `hardware.graphics.{enable,enable32Bit}`, `hardware.nvidia.{modesetting,powerManagement}.enable`, `boot.kernelParams = ["nvidia-drm.modeset=1"]`).
All driver knobs use `lib.mkDefault`, so a downstream `system.nix` can pin a beta driver or flip to the open kernel module without forking the module.
**You still have to add bus IDs** — they're per-machine and can't be derived from any flag. Find them with `lspci -D | grep -E 'VGA|3D'`, then in your `/etc/nixos/system.nix`:
```nix
hardware.nvidia.prime = {
offload.enable = true;
offload.enableOffloadCmd = true;
intelBusId = "PCI:0:2:0"; # or `amdgpuBusId` for AMD iGPU
nvidiaBusId = "PCI:1:0:0";
};
```
Without prime config, supergfxd still switches modes but render-offload via `nvidia-offload <cmd>` is unavailable.
### `nomarchy.system.snapper.enable`
`bool`, default `false`. BTRFS timeline snapshots of `/`. Auto-disables when `/` isn't BTRFS. Includes a `nixos-rebuild-snap` wrapper that takes a "Pre-rebuild" snapshot before each switch.
### `nomarchy.system.hibernation.enable`
`bool`, default `false`. Suspend-then-hibernate on lid close, idle, and power button. Requires a disk swap device or swapfile sized to at least RAM — zRAM alone is not enough.
### `nomarchy.system.hibernation.idleMinutes`
`int`, default `30`. Idle minutes before suspend-then-hibernate fires.
### `nomarchy.system.laptop.enable`
`bool`, default `nomarchy.system.formFactor == "laptop"`. Laptop power preset: TLP (with sane AC/battery governors and ThinkPad-style 75/80 charge thresholds), `services.upower`, `services.thermald` (gated by `laptop.thermald`), and a brightnessctl udev rule so the existing `nomarchy-brightness-{display,keyboard}` scripts run without root. Force-disables `services.power-profiles-daemon` (mutually exclusive with TLP) — to use PPD instead, set `laptop.enable = false` and wire it yourself. Lid-close action defers to `nomarchy.system.hibernation.enable`: `suspend-then-hibernate` when on, `suspend` otherwise. Charge thresholds are only honored on supported hardware (ThinkPad, some ASUS); harmless warning elsewhere.
### `nomarchy.system.laptop.thermald`
`bool`, default `true` on x86_64. Enables `services.thermald` (Intel thermal daemon). Harmless no-op on AMD; gated off on aarch64.
### `nomarchy.system.desktop.enable`
`bool`, default `nomarchy.system.formFactor == "desktop"`. Desktop preset: pins `powerManagement.cpuFreqGovernor` to `"performance"` (via `mkDefault`) and enables `services.zfs.autoScrub` + `services.zfs.trim` so a future ZFS pool gets sensible maintenance without further config. The ZFS knobs are no-ops until you add `boot.supportedFilesystems = [ "zfs" ]` (plus `networking.hostId`) and a pool. Battery-widget filtering is handled by `formFactor` itself, so this preset doesn't repeat it.
### `nomarchy.system.accessibility.enable`
`bool`, default `false`. Accessibility preset: enables `services.gnome.at-spi2-core` (AT-SPI2 framework), installs `pkgs.orca` (screen reader) into `environment.systemPackages`, and sets `XCURSOR_SIZE` to `accessibility.cursorSize`. Off by default — accessibility is a personal preference, not hardware-derived. The Hyprland-side bits (slower key-repeat, Orca launch keybinding, high-contrast palette) are a separate roadmap item.
### `nomarchy.system.accessibility.cursorSize`
`int`, default `32`. `XCURSOR_SIZE` when `accessibility.enable = true`. NixOS default is 24; 32 is a safer floor for low-vision users.
### `nomarchy.system.gaming.enable`
`bool`, default `false`. Gaming preset: enables `programs.steam` (with `remotePlay` and `localNetworkGameTransfers` firewall holes opened by `mkDefault`), `programs.gamemode` (the launching user must be in the `gamemode` group), and `services.flatpak`. The flathub remote isn't added declaratively — after first boot, run `flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo`. Pair with the home-side companion `nomarchy.gaming.enable` for the Hyprland fullscreen-on-Steam-launch window rule.
### `nomarchy.system.containers.enable`
`bool`, default `false`. Rootless Podman with Docker compatibility (`docker``podman`), plus `podman-compose`, `podman-tui`, and `dive`.
### `nomarchy.system.virtualization.libvirt.enable`
`bool`, default `false`. libvirt daemon, virt-manager, and OVMF. Add your user to the `libvirtd` group.
### `nomarchy.system.virtualization.docker.enable`
`bool`, default `false`. Docker daemon and docker-compose. Add your user to the `docker` group.
### `nomarchy.system.keyring.enable`
`bool`, default `true`. Auto-unlock GNOME Keyring at login and route SSH keys through `gcr-ssh-agent`. On by default — near-universal QoL improvement.
### `nomarchy.system.inputMethod.enable`
`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.
### `nomarchy.hardware.isT2Mac`
`bool`, default `false`. Apple T2 MacBook fixes — IOMMU kernel params, `apple-bce` module, brcmfmac feature mask.
### `nomarchy.hardware.isFramework`
`bool`, default `false`. Framework laptop QMK HID udev rule.
### `nomarchy.hardware.fwupd`
`bool`, default `false`. Enables `services.fwupd` firmware update service.
### `nomarchy.hardware.hasIPU7Camera`
`bool`, default `false`. Intel IPU7 camera support (kernel modules + firmware).
### `nomarchy.system.impermanence.enable`
`bool`, default `false`. Erase Your Darlings root wipe on boot. Defined in `core/system/impermanence.nix`. The installer writes the flag based on the impermanence prompt.
### `nomarchy.system.impermanence.mainLuksName`
`str`, default `"crypted"`. Name of the `/dev/mapper` entry holding the BTRFS root. The disko layout uses `"crypted"` on single-disk installs and `"crypted_main"` once multiple drives are selected — the installer writes the matching value automatically.
### `nomarchy.system.impermanence.user`
`str`, default `"nomarchy"`. Primary user whose home subset (`.ssh`, `.gnupg`, `.local/share/keyrings`, `Documents`, `Downloads`, `Pictures`, `Videos`, `Projects`) survives the rootfs wipe. Must match the user created via `users.users.<name>` — otherwise the persistence block is silently inert and the user's home directory is wiped on every boot. The installer writes this for you.
---
## Home Manager options (`home.nix`)
### `nomarchy.theme`
`str`, default `"summer-night"`. Active theme name. Available themes are the directories under `themes/palettes/`.
### `nomarchy.panelPosition`
`enum ["top", "bottom"]`, default `"top"`. Waybar panel position.
### `nomarchy.formFactor`
`enum [ "laptop" "desktop" ]`, default `"laptop"`. Mirror of `nomarchy.system.formFactor`. Filters laptop-only widgets out of waybar (battery) when set to `"desktop"`. The installer writes both system and home values together.
### `nomarchy.wallpaper`
`str`, default `""`. Absolute path to a wallpaper override. Empty string means "use the active theme's default wallpaper".
### `nomarchy.toggles.suspend`
`bool`, default `true`. Whether suspend appears in the system menu.
### `nomarchy.toggles.screensaver`
`bool`, default `true`. Whether the screensaver is enabled.
### `nomarchy.toggles.idle`
`bool`, default `true`. Whether the idle lock is enabled (hypridle).
### `nomarchy.toggles.nightlight`
`bool`, default `false`. Enables hyprsunset.
### `nomarchy.toggles.waybar`
`bool`, default `true`. Whether the top bar is deployed at all.
### `nomarchy.nightlightTemperature`
`int`, default `4000`. Nightlight color temperature (Kelvin).
### `nomarchy.hyprland.gaps_in`
`int`, default `5`. Inner gaps.
### `nomarchy.hyprland.gaps_out`
`int`, default `10`. Outer gaps.
### `nomarchy.hyprland.border_size`
`int`, default `2`. Window border width.
### `nomarchy.fonts.monospace`
`str`, default `"JetBrainsMono Nerd Font"`. Used by terminals, VSCode, etc.
### `nomarchy.iconsTheme`
`str`, default derived from the active theme (falls back to `"Yaru-blue"`). GTK/Qt icon theme name. `core/home/state.nix` computes this from the theme's palette metadata; override to pin a specific icon theme regardless of palette.
### `nomarchy.isLightMode`
`bool`, default derived from the active theme. Whether the active theme is a light theme. `core/home/state.nix` computes this from the theme directory; affects nightlight defaults and a few app theme decisions. Override only if you need to force a specific value.
### `nomarchy.cursor.name`
`str`, default `"Bibata-Modern-Ice"`. Cursor theme name.
### `nomarchy.cursor.package`
`package`, default `pkgs.bibata-cursors`. Package providing the cursor theme. Override both `name` and `package` together if you switch themes.
### `nomarchy.configOverrides`
`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.*` is a reserved option surface (currently a no-op).
### `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`.
### `nomarchy.gaming.enable`
`bool`, default `false`. Home-side companion to `nomarchy.system.gaming.enable`. Adds a Hyprland `windowrulev2 = fullscreen, class:^(steam_app_).*$` so games launched through Steam grab the whole screen instead of opening windowed. Set to the same value as the system option; the installer flips both when the Gaming profile is selected.
### `nomarchy.vscode.devExtensions`
`bool`, default `false`. Install Nomarchy's curated VSCode extension pack (Nix, language servers, theme variants).
### `nomarchy.themeLoader.enable`
`bool`, default `true`. Auto-load theme-specific app configs from the active theme's `apps/` directory. Disable if you want to provide your own.
### `nomarchy.themeLoader.apps.btop`
`bool`, default `true`. Deploy the active theme's `apps/btop.theme` to `~/.config/btop/themes/nomarchy.theme`. The only per-app toggle in this group — waybar themes inline from `colorScheme` in `features/desktop/waybar`; kitty and alacritty are themed by stylix targets (`themes/engine/stylix.nix`); mako has no theme integration yet.
### `nomarchy.overrides.enable`
`bool`, default `true`. **Reserved — currently a no-op.** Intended to gate a future file-based override loader (drop a file under `~/.config/nomarchy/overrides/`, have it substitute the bundled default for that path). The option exists so configs that set it don't fail to evaluate; setting it has no effect today. Use `nomarchy.configOverrides` for bulk redirection, or set `xdg.configFile.<path>.source` directly in your `home.nix` for per-file overrides — Nomarchy's defaults use `lib.mkDefault` and yield to higher-priority assignments. Tracked in `docs/ROADMAP.md`.
### `nomarchy.overrides.paths`
`attrsOf path`, default `{}`. **Reserved — currently unused.** Will be populated by the future override loader.
---
## Examples
### Minimal `system.nix` for a desktop with Cloudflare DNS, Snapper, and rootless Podman
```nix
{
nomarchy.system = {
dns = "Cloudflare";
formFactor = "desktop";
snapper.enable = true;
containers.enable = true;
};
}
```
### Minimal `home.nix` for a Tokyo-night user with custom gaps and opencode
```nix
{
nomarchy = {
theme = "tokyo-night";
hyprland.gaps_in = 8;
hyprland.gaps_out = 16;
apps.opencode.enable = true;
};
}
```
### Ship your own Hyprland keybindings instead of Nomarchy's defaults
Nomarchy deploys its `bindings.conf` with `lib.mkDefault`, so a higher-priority assignment from your own `home.nix` wins:
```nix
{
xdg.configFile."hypr/bindings.conf".source = ./my-bindings.conf;
}
```
The same pattern works for any file Nomarchy deploys via `xdg.configFile.<path>.source = lib.mkDefault …` — point at your own file and skip the default.
---
## Where these are defined
- `core/system/options.nix` — most `nomarchy.system.*` options
- `core/system/hardware.nix``nomarchy.hardware.*`
- `core/system/impermanence.nix``impermanence.enable`
- `core/home/options.nix` — most home-side `nomarchy.*` options
- `core/home/overrides.nix``nomarchy.overrides.*` (reserved; currently no-op)
- `themes/engine/loader.nix``nomarchy.themeLoader.*`
- `features/apps/vscode.nix``nomarchy.vscode.*`

209
docs/ROADMAP.md Normal file
View File

@@ -0,0 +1,209 @@
# Nomarchy Roadmap
This is the mid-term plan for Nomarchy. It exists so future sessions — human or agent — can pick up work without re-deriving context. Items move from **Now****Next****Later** as priorities shift, and from any column into **Shipped** at the bottom when done. There are no dates: ship-when-ready.
If you're new here, also read [`docs/STRUCTURE.md`](STRUCTURE.md) and [`docs/OPTIONS.md`](OPTIONS.md). Existing-NixOS users should also read [`docs/MIGRATION.md`](MIGRATION.md).
## 1. Vision & guardrails
Nomarchy is a NixOS-based distribution that gives you a polished Hyprland desktop (Hyprland + waybar + walker + a curated theming engine) on a strictly declarative, flake-based foundation. Goal: power-user polish without giving up reproducibility.
Guardrails (apply when adding anything):
- **Declarative-first.** No imperative state in `core/`. Anything mutable lives in `~/.config/nomarchy/state.json` or in NixOS options.
- **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.
- **Opt-in by default.** New features default off (or default to the existing behavior). The installer can flip defaults for the user, but the option must read sensibly when set by hand.
- **Reuse before invent.** Before adding a script, grep `core/system/scripts/`, `features/scripts/utils/`, and `themes/engine/scripts/` — there are ~155 of them, and many of the things you want already exist.
## 2. Now / Next / Later board
### Now (ready to pick up)
- **Installer: "What's installed?" summary on first boot.** Surface what the installer actually wrote (theme, font, profiles, drives, form factor) from `state.json` + `nomarchy-system-scripts` introspection so the user can verify before they start customising. Detail in [Pillar 4](#4-pillar-installer).
- **Installer: optional non-LUKS branch.** Let users explicitly opt out of FDE during install. Detail in [Pillar 4](#4-pillar-installer).
- **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.
### Next (bigger lifts that build on Now)
- **Accessibility — home-side companion.** Hyprland-side bits the system preset can't reach: slower `input.repeat_rate` / `repeat_delay` defaults, `SUPER+ALT+S` keybinding to launch Orca, and a high-contrast palette under `themes/palettes/`. Gated on a new `nomarchy.accessibility.enable` mirror of the system option.
- **Gaming — declarative flathub remote.** `services.flatpak.enable` doesn't ship a declarative remote API in nixpkgs. Either add the `flatpak-managed-install` overlay, write a one-shot systemd unit that runs `flatpak remote-add --if-not-exists flathub …`, or surface the manual step in `nomarchy-welcome`.
- **Plymouth theme variants per palette.** Currently one Plymouth theme; could template per-palette so the boot splash matches the active theme.
- **Implement `nomarchy.overrides.*` file-based override loader.** The option surface is declared in `core/home/overrides.nix` but the loader doesn't exist — files dropped under `~/.config/nomarchy/overrides/` are ignored. Spec: when `overrides.enable = true`, for each `xdg.configFile.<path>` Nomarchy deploys with `lib.mkDefault`, check whether `~/.config/nomarchy/overrides/<path>` exists at build time; if so, use it as the source. Requires deciding whether discovery happens at activation time (cheap, but rebuild-required to pick up new files) or via a populated `paths` attrset (Nix-side, evaluated once per rebuild). Until shipped, document the per-file workaround (`xdg.configFile.<path>.source = ./mine`) in OPTIONS.md.
### 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.
- **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.
- **Forgejo release pipeline.** `vYY.MM.x` tags matching the upstream NixOS channel; the pipeline pushes the three ISOs and an updated `flake.lock` snapshot.
- **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.
- **Consolidate palette imports in `flake.nix` via `nomarchyLib`.** `flake.nix:79-80` re-imports `./themes/palettes` and recomputes `themeNames` even though `lib/default.nix` already exports both. Two computations, same result today — drift risk tomorrow. Import `nomarchyLib = import ./lib { inherit lib; }` once and use `nomarchyLib.{palettes,themeNames}` to make `lib/default.nix` the single source of truth for the theme list.
- **Decide what to do about `features/apps/chromium/Default/Preferences`.** The module deploys a static 204-byte `Default/Preferences` (setting `extensions.theme.use_system = false`, `browser.theme.color_scheme = 2`) into chromium's mutable profile directory via Home Manager symlink. Chromium expects to write that file at runtime, so either the symlink is silently replaced on first save (losing the static defaults) or the write fails silently. The real chromium theming work happens via managed policies in `core/system/browser.nix`. Probably the user-profile deployment should be removed; needs someone with chromium-internals knowledge to confirm before deletion.
- **`nomarchy-menu` "Setup → Config" pattern edits Nix-managed files.** Five of the nine entries in `show_setup_config_menu` open files that Home Manager generates from declarative options (`hypridle.conf` from `services.hypridle.settings`, `hyprsunset.conf` from `services.hyprsunset`, `walker/config.toml` from `programs.walker.config`, `waybar/config.jsonc` from `programs.waybar.settings`, `hyprland.conf` from `wayland.windowManager.hyprland.settings`). The menu pattern is "edit then restart", but a `home-manager switch` clobbers the edit and the file goes back to the Nix-generated content. Two more entries (`hyprlock.conf`, `swayosd/config.toml`) point at paths the modules don't deploy at all — `open_in_editor` creates an empty file. Either remove these entries (the user should edit the Nix module), or rewire each to open the matching Nomarchy option doc / module file path, or document the ephemerality in `notify-send` when entering edit mode. Picked up during Pillar 8 / Component 8.
- **`themes/templates/*.tpl` — prune the truly dead, document the rest.** Eleven mustache-style templates deployed to `~/.local/share/nomarchy/templates/` via `themes/engine/files.nix`. Originally flagged as "no script consumes them" — that was wrong: `themes/engine/scripts/nomarchy-theme-set-templates` (called at the end of `nomarchy-theme-set`) reads them, substitutes palette colors, and writes the output to `~/.config/nomarchy/current/theme/<file>` only when no file is already there. Now categorise: **(a) functionally dead** — `alacritty.toml.tpl`, `btop.theme.tpl`, `chromium.theme.tpl`, `swayosd.css.tpl` all produce files at paths nothing reads (alacritty + swayosd are Stylix/Nix-themed; btop reads from `~/.config/btop/themes/nomarchy.theme` not from the theme symlink; chromium.theme references were deleted in `9b06b6c`). **(b) superseded** — `kitty.conf.tpl` and `ghostty.conf.tpl` are now generated by `themes/engine/files.nix` (commit `8d3ce2d`); the template path skips harmlessly via the "if not exists" check. **(c) still relevant** — `hyprland.conf.tpl`, `hyprlock.conf.tpl`, `obsidian.css.tpl`, `keyboard.rgb.tpl`, `hyprland-preview-share-picker.css.tpl` (this last one was orphaned when the share-picker dir was deleted in `20de3d4` — verify before keeping). Delete (a) + (b) + the orphan share-picker tpl; document the rest in `docs/creating-themes.md` as the layered override surface they actually are.
- **Move `programs.uwsm` Hyprland session out of `core/system/virtualization.nix`.** Session-manager config is wired in the virtualization module by historical accident — it's loaded unconditionally on every install and has nothing to do with libvirt/docker. Move to a dedicated `core/system/session.nix` (or fold into the Hyprland feature module) so the location matches the responsibility.
- **Route installer keymap into Hyprland's Wayland session.** `core/home/config/nomarchy/default/hypr/input.conf:3` hardcodes `kb_layout = us`. The installer writes `services.xserver.xkb.layout = "$KEYMAP_LAYOUT"` (and `console.keyMap`), but Hyprland reads its own input config on native Wayland, so a non-US user's chosen layout works in XWayland apps and the TTY console but not in native Wayland apps — surprising and inconsistent. Fix paths: (a) template `input.conf` from a new `nomarchy.keymap.{layout,variant}` home option that the installer writes alongside `formFactor`, or (b) propagate `XKB_DEFAULT_LAYOUT` via session env so Hyprland's fallback kicks in. Option (a) is the durable fix; needs the installer's heredoc to add the option write.
- **Make `nomarchy.toggles.waybar` a Nix-level gate, or document it as runtime-only.** Today the toggle is exported as `NOMARCHY_TOGGLE_WAYBAR` env, consumed only by `nomarchy-toggle-waybar` (which pkill/exec's at runtime). The Nix module always sets `programs.waybar.enable = lib.mkDefault true`, so waybar comes back on every rebuild/reboot regardless of the toggle. Inconsistent with `toggles.idle`, which correctly gates `services.hypridle.enable`. Either gate `programs.waybar.enable` on the toggle (persistent) or rename the option to make its session-only nature obvious.
- **Package missing VSCode theme extensions via `extensionFromVscodeMarketplace`.** 15 of the 21 palettes that ship a `themes/palettes/<theme>/apps/vscode.json` declare a theme extension that isn't in `pkgs.vscode-extensions` — including `sainnhe.everforest` which is the default `summer-night` palette's theme. With the `577b3ae` fix in place, the 6 nixpkgs-packaged extensions install by default (catppuccin, catppuccin-latte, nord, tokyo-night, rose-pine, gruvbox), but the other 15 (`sainnhe.everforest`, `qufiwefefwoyn.kanagawa`, `monokai.theme-monokai-pro-vscode`, `oldjobobo.{lumon,miasma,retro-82}-theme`, `Bjarne.{ethereal,hackerman,vantablack,white}-nomarchy`, `shadesOfBuntu.flexoki-light`, `jovejonovski.ocean-green`, `TahaYVR.matteblack`) still leave `workbench.colorTheme` referencing an unloaded theme, so VSCode silently falls back. Fix: extend `features/apps/vscode.nix` to look up the active palette's extension via `pkgs.vscode-utils.extensionFromVscodeMarketplace { publisher; name; version; sha256; }` — each entry pinned by hash. Could be table-driven in `lib/` so a new palette only needs to add a row.
- **Reconcile nightlight: systemd-managed hyprsunset vs `pkill`/`hyprctl exec` toggle.** `features/desktop/nightlight.nix` always sets `services.hyprsunset.enable = lib.mkDefault true` and bakes the temperature (`toggles.nightlight ? nightlightTemperature : 6500`) into `extraArgs` at Nix-eval time. The toggle script `themes/engine/scripts/nomarchy-toggle-nightlight` bypasses systemd entirely: `pkill hyprsunset` on disable, `hyprctl dispatch exec hyprsunset --temperature 4000` on enable — racing the systemd-managed instance and ignoring the user's chosen `nightlightTemperature`. The comment "Always enabled, we control via IPC and state" doesn't match the implementation (there's no IPC; the temperature is rebuild-time, not runtime). Pick one: (a) toggle stays as `hyprctl dispatch hyprsunset temperature ${T}` (proper IPC, no daemon restart), with Nix dropping `extraArgs` to just `--temperature 6500` on boot; or (b) gate `services.hyprsunset.enable` on the toggle and run `systemctl --user restart hyprsunset` from the script. Both also need the script to read `nightlightTemperature` instead of hardcoding 4000.
## 3. Pillar: Script & menu audit
Nomarchy ships **~155** `nomarchy-*` scripts across three directories, plus a 379-line `nomarchy-menu` with 23 submenu functions. Some are first-class Nomarchy work; some are direct Omarchy ports that haven't been adapted; some are dangling references the menu calls but no script implements (e.g. `nomarchy-backup`, `nomarchy-debug`, `nomarchy-pkg`, `nomarchy-pkg-aur-add`, `nomarchy-plymouth`, `nomarchy-refresh-hyprland`, `nomarchy-reinstall`, `nomarchy-rollback`, `nomarchy-screenrecord-filename`, `nomarchy-theme`, `nomarchy-update-firmware`, `nomarchy-upload-log`, `nomarchy-version`, `nomarchy-wallpaper`, `nomarchy-skill`, `nomarchy-luks`).
This pillar fixes that. It runs as two phases.
### Phase A — Inventory & triage
Lands as a single PR. Output is `docs/SCRIPTS.md` populated with rows for every script and every menu item.
1. Run a generator (one-shot helper, doesn't have to be checked in) that produces three lists:
- All `nomarchy-*` scripts under `core/system/scripts/`, `features/scripts/utils/`, `themes/engine/scripts/`.
- All `nomarchy-*` *callers* (grep `core/`, `features/`, `themes/`, `installer/`, `bin/`).
- The set difference (orphaned callers ↔ unreferenced scripts).
2. Walk `features/scripts/utils/nomarchy-menu` and list every menu entry with its target script.
3. Tag each row with a status:
- `kept` — works on Nomarchy, no change needed.
- `port-from-omarchy` — exists upstream, needs adapting (drop pacman/yay/AUR, repath to NixOS, talk to `nomarchy.system.*` options).
- `delete-dead` — neither used nor needed; remove and update callers.
- `stub-with-notify` — temporarily replace with a `notify-send "Not yet implemented in Nomarchy"` so the menu stops looking broken until the work is scheduled.
- `unknown` — needs a deeper look before tagging.
4. The completed table lives at [`docs/SCRIPTS.md`](SCRIPTS.md). The roadmap links to it; this section just sets the methodology.
### Phase B — Adapt or remove
Lands as PR batches of ~10 scripts each, branch named `wave/audit-<batch>`. Per script:
- For `port-from-omarchy`: rewrite the script for Nomarchy paths (`/etc/nixos`, `nixos-rebuild`, `home-manager`, no Arch idioms), wire it into `nomarchy.system.*` where applicable, and update every caller (menu, waybar, keybindings).
- For `delete-dead`: `git rm` the script *and* fix every caller — a `find` + `sed` pass against `nomarchy-menu`, every `*.conf`, and every nix file.
- For `stub-with-notify`: write the one-liner stub in place. The roadmap row stays open until the real implementation lands.
Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes, and reviewers spot-check that no caller still points at a stale name.
## 4. Pillar: Installer
- "What's installed?" summary screen on boot of a freshly-installed system, sourced from `state.json` + `nomarchy-system-scripts` introspection (Now).
- Optional non-LUKS branch in the installer for users who explicitly opt out of FDE (Now).
- Richer disk metadata (Shipped).
- `disko-golden.nix` variants for software-RAID and BTRFS-pool-as-root (Shipped).
- Pre-flight resume polish (Shipped).
- Software-profile multi-select (Shipped).
- Form-factor → laptop preset (Shipped).
## 5. Pillar: Power, hardware, presets
- Auto-detect dGPU presence in `installer/hardware-db.sh` and pre-fill `hardware.nvidia.prime.{intel,nvidia}BusId` in the generated `system.nix` (driver stack itself is Shipped — see entry below).
- Surface support behind `nomarchy.hardware.isSurface` (Later).
- Laptop preset: TLP, upower, brightness, lid, hypridle tuning (Shipped).
- Desktop preset: performance governor, no laptop UI (already filtered), ZFS hooks (Shipped).
- Accessibility preset (Shipped).
- Gaming preset (Shipped).
- Vendor matchers in `installer/hardware-db.sh` (Shipped — ROG Ally added; Surface/Framework/Lenovo entries corrected; Steam Deck + Snapdragon X documented as nixos-hardware-unsupported. CI now lints DB references).
## 6. Pillar: Onboarding & docs
- `nomarchy-welcome` first-run wizard (Shipped).
- `docs/KEYBINDINGS.md` auto-generator (Shipped).
- `docs/TROUBLESHOOTING.md` (Shipped).
- `docs/index.md` / README docs index (Shipped — `README.md` links every doc in `docs/`).
- `nomarchy-manual` — opens the local `~/.local/share/nomarchy/README.md` via `xdg-open` (Shipped).
## 7. Pillar: Test, CI, release
- Forgejo Actions workflow:
- on every push to `main`: `nix flake check` (≈ what we run by hand today).
- on every PR: also build all three ISOs (cache hit on most of them).
- on tag `vYY.MM.x`: publish ISOs as release artefacts.
- Versioning scheme: `vYY.MM.x` matching the upstream NixOS channel (e.g. `v25.11.3`).
- `nixosTest` per palette: boots `default` in a VM, screenshots the SDDM splash and the Hyprland desktop, diffs vs golden. Failure surfaces as CI red.
- A small `bin/utils/nomarchy-bench-iso-build` that records ISO build time + size into a per-commit JSON so we notice regressions.
## 8. Pillar: QA audit — features & components
Nomarchy now spans an installer, ~159 `nomarchy-*` scripts, a Hyprland desktop stack (Hyprland + waybar + walker + nightlight + idle), curated apps, a 22-palette theme engine, and two ISO hosts. Pillar 3 audited script *existence*; this pillar audits feature *behavior*. The goal: walk every shipped feature end-to-end on a real install, fix every bug or surprise inline when small, and capture the rest as new roadmap rows.
Runs as **per-component sweeps**. One PR per component, branch `wave/qa-<component>`. Don't grow scope mid-PR — bugs that need a new option, refactor, or missing module become a new **Now**/**Next** row.
Components (each is one sweep):
1. **Installer**`installer/install.sh`, `installer/hardware-db.sh`, disko configs. Fresh install + `--resume` + `--dry-run`, on laptop and desktop, with FDE (non-LUKS branch is Later). Verify every generated file (`flake.nix`, `system.nix`, `home.nix`, `hardware-selection.nix`, `state.json`) is correct and idempotent.
2. **First-boot UX**`nomarchy-welcome`, generated `home.nix`, SDDM and Plymouth metadata, default theme/font/panel position. Re-run on a clean VM; note every prompt that confuses and every default that's wrong.
3. **Core system modules**`core/system/*` (laptop, desktop, accessibility, gaming, hybridGPU, impermanence, network, hardware, branding). For each: enable → rebuild → observe the claimed effect → disable → rebuild → observe it's gone. Cross-check against `docs/OPTIONS.md`.
4. **Core home modules**`core/home/*` (options, state, behavior, overrides, deployed config). Verify every home-side `nomarchy.*` option does what its description claims; confirm `~/.config/nomarchy/overrides/` actually overrides.
5. **Desktop stack** — Hyprland (keybindings, window rules, monitors, input), waybar (every module × both panel positions × both form factors), walker (every launcher mode), idle, nightlight, notifications (mako). Reconcile `docs/KEYBINDINGS.md` against runtime.
6. **Apps**`features/apps/*`. Each app: launches, themed via Stylix, configured as expected. Catches the "we package it but nobody configured it" class.
7. **Theme engine + palettes**`nomarchy-theme-set` across all 22 palettes, font and wallpaper switchers, light-mode toggle. Verify per-palette Stylix targets render correctly across SDDM, Plymouth, GTK, Qt, terminals, browsers, waybar, walker.
8. **Scripts (runtime behavior)** — Pillar 3 confirmed existence; this sweep runs every user-visible script (especially every `nomarchy-menu` entry) on current NixOS and confirms it actually does the thing.
9. **ISOs** — boot `nomarchy-installer` and `nomarchy-live`; verify the `nomarchy-test-live-iso` flow; check the installer ISO ships every tool `install.sh` calls (regression class: `hardware-db.sh` missing, already shipped).
10. **Lib + state schema**`lib/state-schema.nix`, color resolution, path helpers. Cross every codepath that produces `state.json` (installer, welcome wizard, hand-edit) against the schema; confirm bad inputs are rejected with a useful message.
Per-PR deliverable:
- PR body lists what was tested, what was broken, what was fixed inline, what was deferred (with the new roadmap row linked).
- Doc updates ride with the change per `docs/AGENT.md` §5.4.
- Don't bundle fixes across components — keep one component per branch so reviewers can spot-check end-to-end without context-switching.
Pillar is **done** when every component has a closed `wave/qa-<component>` PR and the roadmap captures every deferred finding.
## 9. 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.
- **Living roadmap.** When an item ships, move it to the **Shipped** section at the bottom of this file rather than deleting it. Future-us gets a free changelog.
- **Plan files live separately.** Detailed implementation plans (the per-feature design docs Claude writes in plan mode) belong under `~/.claude/plans/` per session, not in the repo. The roadmap is the durable reference; plan files are working notes.
- **Don't widen scope mid-PR.** If the audit reveals a missing feature, file a new roadmap row, don't graft it onto the current PR.
## Shipped
(Move items here when they land — keep them brief, link the commit/PR.)
- _2026-05-21_ — **Pillar 8 / Component 9 (ISOs): closeout — Pillar 8 code-audit phase complete.** Code-review-shaped sweep over `hosts/{nomarchy-installer,nomarchy-live}.nix`, the `installation-cd-minimal`/`installation-cd-graphical-base` module chain, and the four ISO build/test scripts. Two minor fixes inline: `nomarchy-build-iso` and `nomarchy-build-live-iso` both ran under `set -e` but then wrapped `nix build` in an `if [ $? -eq 0 ]` block — the `else` branch printing "Error: ISO build failed." was unreachable because `set -e` aborts before the conditional. Removed the dead branches (behaviour identical: the user sees `nix build`'s own error and the script exits). Regression-class check (`hardware-db.sh` precedent): cross-referenced every tool `install.sh` calls against the installer host's `environment.systemPackages` chain — `gptfdisk` (sgdisk) is provided by upstream `profiles/base.nix:21` which `installation-cd-base.nix` chains, `jq` is wrapped in the `nrun` nix-run fallback, and every other direct call (`wipefs`, `dd`, `parted`, `partprobe`, `cryptsetup`, `disko`, `nixos-{install,enter,rebuild}`, `loadkeys`, `timedatectl`, `nmtui`) resolves via either the explicit host packages or the standard base. `nomarchy-live` host shape verified: multi-GPU initrd modules + Xwayland video drivers cover both real hardware and QEMU; auto-login + passwordless sudo + helpful TTY MOTD + Hyprland on-boot exec to a terminal at the install command. `nomarchy-test-live-iso` walks four OVMF candidate paths with KVM detection. With this entry, every code-shaped audit in Pillar 8 has shipped (Components 110); the Now-column "Full QA audit" item moves out, replaced by a runtime-verification punch-list entry covering the cross-component "needs runtime verification" notes from each closeout.
- _2026-05-21_ — **Pillar 8 / Component 8 (Scripts runtime behavior): closeout.** Code-review-shaped sweep over `features/scripts/utils/nomarchy-menu` (382 lines, 23 submenu functions), every script referenced from those submenus, the schema↔script field-name cross-check, and cross-cutting typo/stale-reference patterns. Four real fixes inline: **(1)** `nomarchy-menu:70` — "Learn → Nomarchy" still called `nomarchy-launch-webapp https://learn.omacom.io/2/the-nomarchy-manual` (an upstream Omarchy URL — the same one fixed in `nomarchy-manual` back on 2026-05-18). Now calls `nomarchy-manual`, which opens the local docs index. **(2)** `nomarchy-menu:179` — "Style → Hyprland" opened `~/.config/hypr/looknfeel.conf`, a path nothing deploys; the actual file lives at `~/.config/nomarchy/default/hypr/looknfeel.conf` (sourced via the chain from `nomarchy.conf`). Updated the path. **(3)** `nomarchy-menu:258``*Overrides*) xdg-open ~/.config/nomarchy/overrides/` case branch with no matching menu option, dead code anticipating the still-unimplemented `nomarchy.overrides.*` loader. Removed (will reappear with the option when the loader ships). **(4)** `nomarchy-theme-bg-next:12``jq -r '.theme // "nord"'` defaulted to `"nord"` if `.theme` was missing, while `lib/state-schema.nix:17` defines `"summer-night"` as the schema default. On a fresh-or-empty `state.json` the script looked for backgrounds under `palettes/nord/` while the rest of the system treated `summer-night` as active. Matched to the schema default. Cross-cutting sweeps came back clean: no `$NN[A-Z]+` env var typos elsewhere (the prior pair fixed in `40b6212` was the lot), no references to scripts deleted in earlier Pillar 3 batches (`nomarchy-restart-{hyprctl,mako,tmux}`, `nomarchy-battery-present`, `nomarchy-sudo-keepalive`, `nomarchy-rollback`, `nomarchy-snapshot`, `nomarchy-migrate-state`, `nomarchy-config-direct-boot`, `nomarchy-npx-install`, `nomarchy-webapp-handler-{hey,zoom}`), no stray `omarchy`/`omacom` strings outside historical roadmap entries, and every `state.json` field-write resolves against `lib/state-schema.nix` (or the documented off-schema `welcome_done`). One UX-shaped pattern bug logged separately to Later: `show_setup_config_menu` edits Nix-managed files that get clobbered on the next `home-manager switch`. Runtime verification (run every user-visible menu entry and confirm it does the thing) remains on the user.
- _2026-05-21_ — **Pillar 8 / Component 7 (Theme engine + palettes): closeout.** Code-review-shaped sweep across `themes/engine/{stylix,stylix-compat,loader,files,scripts}.nix`, the 23 theme-engine scripts, and the 21 palettes' file completeness. Three real fixes inline + targeted dead-surface cleanup: **(1)** `nomarchy-theme-set` printed a warning when the named theme directory didn't exist but continued executing — wrote the bad name into `state.json` and ran `nomarchy-env-update` on a broken state. Now `exit 1` after the warning. **(2)** `nomarchy-theme-bg-set` (called by the walker background-selector menu and by the `nomarchy-wallpaper` CLI) updated the live `~/.config/nomarchy/current/background` symlink + restarted swaybg but never wrote `state.json` — so every wallpaper picked via either path silently reverted to the theme default on the next `home-manager switch` (`themes/engine/files.nix` re-resolves `nomarchy.wallpaper` at every rebuild). Now writes the chosen path into `state.json.wallpaper`, mirroring `nomarchy-theme-bg-next`. Added a file-exists check so a bogus path fails loudly instead of leaving a dangling symlink + a crashed swaybg. **(3)** Palette tree dead-surface cleanup: deleted `themes/palettes/{flexoki-light,lumon,retro-82,rose-pine}/apps/chromium.theme` (9-byte RGB strings nothing reads — chromium is themed via managed policies in `core/system/browser.nix`, not per-palette files) and `themes/palettes/summer-day/apps/kitty/{kitty.conf,everforest-light.conf}` (a 76KB stray kitty config at the wrong nested path, superseded by the `kitty.conf` generator added in `8d3ce2d`). Total: 6 files / 2210 lines. Updated the misleading comment in `nomarchy-themes-prebuild` ("the installer wires this up") to reflect reality (the installer only tips the user to run it). Updated the `themes/templates/*.tpl` Later row with a fact-check + concrete categorisation — the templates ARE consumed by `nomarchy-theme-set-templates`, but most write to paths nothing reads or are now superseded by Nix-side generators. Palette completeness matrix: all 21 palettes have `colors.toml`, `backgrounds/`, `icons.theme`, and `apps/`; 5 carry the `light.mode` marker (catppuccin-latte, flexoki-light, rose-pine, summer-day, white); only tokyo-night ships `keyboard.rgb` for the ASUS ROG path, and the keyboard-set chain isn't wired into `nomarchy-theme-set` so it stays manual — niche enough to leave. Runtime verification (switch through all 22 palettes and eyeball SDDM + Plymouth + GTK + Qt + terminals + browsers + waybar + walker rendering) remains on the user.
- _2026-05-21_ — **Pillar 8 / Component 6 (Apps): closeout.** Code-review-shaped sweep over `features/apps/{alacritty,btop,chromium,elephant,ghostty,kitty,lazygit,opencode,swayosd,tmux,vscode,walker}`. Three real theming bugs fixed inline: **(1)** `features/apps/kitty/config/kitty.conf:1` and `features/apps/ghostty/config/config:2` referenced palette-specific include files (`~/.config/nomarchy/current/theme/{kitty,ghostty}.conf`) that didn't exist for any of the 22 palettes — kitty include failed silently, ghostty's was optional (`?`-prefix), and both terminals rendered with their built-in defaults regardless of the active Nomarchy palette. Stylix's `kitty.enable = true` was a no-op because the module uses `xdg.configFile` instead of `programs.kitty`; ghostty has no Stylix target. Added theme-engine generators in `themes/engine/files.nix` mirroring the existing `waybar.css` pattern, mapping `palette.base*` to kitty/ghostty color directives. **(2)** `features/apps/btop/config/btop.conf:5` set `color_theme = "current"` but `themes/engine/loader.nix:72` deploys the active palette's btop theme to `~/.config/btop/themes/nomarchy.theme` — name mismatch, btop fell back to its built-in Default theme on every palette. Renamed to `"nomarchy"`. **(3)** `programs.vscode.profiles.default.userSettings.workbench.colorTheme` was set unconditionally from `themes/palettes/<theme>/apps/vscode.json`, but the matching theme extensions were bundled with `devExtensions` (default `false`) — so VSCode silently fell back to its built-in theme out of the box on every palette. Split `themeExtensions` (always-on, covers the 6 palettes whose theme extension is in nixpkgs) from `devExtensions` (opt-in). The remaining 15 palettes — including the default `summer-night` (`sainnhe.everforest`) — still break because their theme extensions aren't packaged in nixpkgs; logged as a new Later row. Chromium static `Default/Preferences` symlink already had an open Later row; verified the file's contents are duplicate of the managed-policy intent in `core/system/browser.nix`, so the existing entry's hypothesis is correct — left for the user to greenlight deletion. alacritty (Stylix-themed via `programs.alacritty.settings`), elephant (no UI), swayosd (base16 inline), walker (covered in Component 5), lazygit + tmux (terminal ANSI inheritance, transitively fixed by the kitty/ghostty changes), and opencode (minimal opt-in config) are healthy. Runtime verification (launch each app on each palette and eyeball the theming) remains the user's responsibility.
- _2026-05-21_ — **Pillar 8 / Component 5 (Desktop stack): closeout.** Code-review-shaped sweep over Hyprland, waybar, walker, hypridle, hyprsunset, mako, KEYBINDINGS.md (the runtime-rendering subset — waybar across panel positions × form factors × all 22 palettes, walker launcher modes, hypridle timeout feel — stays on the user). Five real bugs fixed inline: **(1)** 9 of 17 `~/.config/nomarchy/default/hypr/apps/*.conf` window-rule files were deployed but never sourced, including `system.conf` (the `tag +floating-window` rules every TUI helper class relies on + `class:org.nomarchy.screensaver` fullscreen rule that hypridle's 150s on-timeout depends on) and `pip.conf` (the PiP pin/size rules). `apps.conf` now sources all 17. **(2)** Two `$NNOMARCHY_TOGGLE_*` typos (double-N) in `nomarchy-menu:330` and `nomarchy-launch-screensaver:16` made `toggles.suspend` and `toggles.screensaver` vacuous — Suspend always showed in the system menu and the screensaver always launched at idle regardless of the documented option. **(3)** 4 broken per-palette waybar `style.css` overrides (`catppuccin`, `lumon`, `nord`, `retro-82`) fully replaced the default style with 214 lines of only `@define-color` declarations — picking those palettes produced a waybar with zero structural styling. Default style already imports per-palette colors via `themes/engine/files.nix`-generated `theme/waybar.css`, so deletion restores correct rendering; `summer-day`/`summer-night` kept as legitimate 100+-line redesigns. **(4)** `core/home/config/nomarchy/default/hypr/{bindings,plain-bindings}.conf` were explicitly-labeled deprecated files sourced by nothing (plain-bindings.conf referenced undefined `$terminal`/`$browser`/etc. Hyprland vars) — deleted; `docs/SCRIPTS.md` regenerated to drop stale callers and incidentally corrected 4 Origin columns whose scripts moved from `core/system/scripts/` to `features/scripts/utils/`. **(5)** Mako post-fix (commit `2a301a0`) verified: deployment + the 4 referenced scripts (`nomarchy-notification-dismiss`, `nomarchy-launch-wifi`, `nomarchy-launch-floating-terminal-with-presentation`, `nomarchy-menu-keybindings`) all resolve. Two structural inconsistencies logged to Later: keymap routing (already in 72f7e7b) and the new hyprsunset toggle-vs-systemd reconcile. `KEYBINDINGS.md` regenerated with zero diff — generator already covers both binding source locations. Runtime verification (boot live ISO, eyeball waybar/walker/screensaver flows across panel positions and palettes) remains the user's responsibility before declaring Component 5 fully closed.
- _2026-05-18_ — Hardware DB correctness pass + ROG Ally support + CI lint. Audited every `nomarchy-hardware-db` entry against `inputs.nixos-hardware.nixosModules` and found **21 of 43 entries (49%) referenced modules that don't exist**`microsoft-surface-pro-8`, `lenovo-thinkpad-x1-carbon-gen11`, `framework-13-11th-gen-intel`, etc. were all eval-time failures waiting for a real user. Rewrote the DB to use only valid module names: Framework gens dropped the "13-" prefix in nixos-hardware (`framework-11th-gen-intel`, not `framework-13-11th-gen-intel`); ThinkPad X1 modules are `x1-Nth-gen`, not `x1-carbon-genN`; Surface Pro 6/7/8/10 all share `microsoft-surface-pro-intel`; Surface Book / Intel Surface Laptop have no module (rows dropped, generic detection still emits sensible `common-pc-laptop` + cpu/gpu). Added matchers for **ROG Ally** (RC71L / RC72LA / "ROG Ally" via `asus-ally-rc71l`). Documented Steam Deck and Snapdragon X as nixos-hardware-unsupported in a footer comment (Steam Deck → Jovian-NixOS; Snapdragon X → installer is x86_64 only). Added a CI step (`.forgejo/workflows/check.yml`) that fails on any DB entry whose module name isn't in `nixos-hardware.nixosModules` — closes this regression class.
- _2026-05-18_ — `nomarchy-manual` re-targeted at local docs. The script's `xdg-open` previously pointed at `https://learn.omacom.io/2/the-nomarchy-manual` — an upstream Omarchy URL that opened an unrelated page when users hit the menu's Help entry. Now opens `~/.local/share/nomarchy/README.md` (the local docs index per `SKILL.md`'s "Out of Scope" note), with a `notify-send` fallback if the source tree isn't synced.
- _2026-05-18_ — Docs hygiene: STRUCTURE.md "Root Directory" + Pillar 6 reality-check. `docs/STRUCTURE.md` listed three top-level files that don't exist (`GEMINI.md`, root-level `STRUCTURE.md`, `TODO.md`) — replaced with an accurate root listing plus a `docs/` sub-tree that names every doc. Pillar 6 in this file had `nomarchy-welcome`, `docs/TROUBLESHOOTING.md`, and the "docs index" bullet still marked Next despite all three shipping on 2026-04-26 — moved to `(Shipped)`. `nomarchy-manual` bullet's "orphaned reference today" claim was stale (the script is called from `nomarchy-menu` and `nomarchy-theme-install`); rewritten to reflect the real remaining issue — its hardcoded `xdg-open https://learn.omacom.io/2/the-nomarchy-manual` is an Omarchy URL.
- _2026-05-18_ — Installer state.json is now schema-driven. Replaced the heredoc in `installer/install.sh` that hardcoded the JSON literal (theme/dns/wifi/features/etc.) with a `nix eval` of `lib/state-schema.nix`'s `system` block, overlaid with the installer-chosen timezone. Closes the last source-of-truth split after the centralization batch — adding a new default in the schema now reaches the installer with no further plumbing. Output is identical modulo alphabetical key ordering (Nix's `builtins.toJSON` sorts keys; toggle scripts read/write via `jq` so it's invisible to them). Dry-run path unchanged (still bind-mounts a fake `/mnt` so the generator's absolute paths resolve correctly). `bash -n` + `shellcheck --severity=error` clean.
- _2026-05-18_ — Complete the hybrid-GPU wiring + fix unoverridable state-derived options. Two related fixes shipped together. **(1)** `nomarchy.system.features.hybridGPU = true` now wires the full NVIDIA driver stack (`services.xserver.videoDrivers = ["nvidia"]`, `hardware.graphics.{enable,enable32Bit}`, `hardware.nvidia.{modesetting,powerManagement}.enable`, `package = nvidiaPackages.stable`, `boot.kernelParams += "nvidia-drm.modeset=1"`) — was previously enabling only `supergfxd` mode-switching while leaving the system with no NVIDIA driver loaded, so mode switches silently no-op'd. All knobs use `lib.mkDefault` so a downstream `system.nix` can pin a beta driver, flip to the open kernel module, etc. Bus-ID prime config (per-machine) stays user-supplied — `docs/OPTIONS.md` has the full recipe. **(2)** Both `core/system/state.nix` and `core/home/state.nix` now use `lib.mkDefault` on every state.json-derived assignment, fixing a class of "I set X in my system.nix but it doesn't take effect" bugs (the state-derived value was at default priority and conflicted with the user's same-priority override). Side-effect cleanup: `core/system/state.nix` now also reads from `lib/state-schema.nix` like `core/home/state.nix` does, completing the schema-centralization started two batches ago. Verified `nix flake check` + an override test that flips hybridGPU via an overlay and confirms the entire driver stack engages.
- _2026-05-18_ — Pillar 4: pre-flight resume polish. Fixed four resume-flow gaps in `installer/install.sh`: (1) `--resume` with a missing state file now errors loudly with a tmpfs explanation instead of silently falling through to a fresh prompt cycle (the most common operator confusion was "rebooted, forgot tmpfs eats /tmp/, watched the installer start over without realising"); (2) on resume, the saved target drive is validated as a block device before any disk-phase step runs — catches the live-ISO USB-unplugged / non-deterministic /dev/sdX class of mid-install failures; (3) `save_state` now stamps an ISO-8601 timestamp and `load_state` shows a `(saved Xm ago)` banner plus a `Target: /dev/X → user @ host` summary line, so the user can `Ctrl-C` if they're resuming onto the wrong host before any destructive prompt fires; (4) `--help` now documents the tmpfs limitation. `shellcheck --severity=error` passes.
- _2026-05-18_ — Declarative-state defaults centralization. Made `lib/state-schema.nix` the single source of truth for every state-default that previously lived in three places (the schema itself, `core/system/options.nix` / `core/home/options.nix` `default = …` clauses, and `core/home/state.nix` `or …` fallbacks). Replaced ~25 hardcoded literals with `schema.<scope>.<key>` reads. Side-effect: fixed a lingering bug where `core/home/options.nix:theme` still defaulted to `"summer-night"` after the system-side was moved to `"nord"` — half the codebase's home option resolved to the wrong theme when state.json was missing/blank. `nix flake check --no-build` confirms zero semantic change for every other field. Doesn't touch the installer-written `state.json` (separate batch — needs schema → JSON generation).
- _2026-05-18_ — Pillar 7 first step: Forgejo Actions CI (eval + lint). New `.forgejo/workflows/check.yml` runs on every push to `main` and every PR: (1) `nix flake check --no-build` to catch eval regressions, (2) `bash -n` + `shellcheck --severity=error` over every `nomarchy-*` bash script (whole-tree, not just changed files — gates branches that bypass the pre-commit hook), (3) `docs/SCRIPTS.md` drift check (fails loudly if a script change didn't regenerate the audit doc). All three checks pass locally on the current tree. Activation requires enabling Actions on the Forgejo repo and registering a `forgejo-runner`; the workflow itself is dormant until then. ISO build job is intentionally deferred — needs a binary cache (Cachix/Attic) to be tractable.
- _2026-05-18_ — **Pillar 3 Phase B: complete.** Final batch (restart/sudo/theme/misc clusters) cleared the last 13 `unused?` rows. Deleted five truly dead scripts: `nomarchy-restart-{hyprctl,mako}` (theme switching calls `hyprctl reload`/`makoctl reload` directly now), `nomarchy-restart-tmux` (one-liner of marginal value), `nomarchy-battery-present` (battery monitor checks `/sys/class/power_supply/BAT*` inline), `nomarchy-sudo-keepalive` (intended-to-be-sourced building block with no users). Surfaced eight useful tools in `SKILL.md` so the audit catches them as `kept` and AI assistants can discover them: `nomarchy-restart-trackpad` (intel_quicki2c reload), `nomarchy-sudo-{passwordless-toggle,reset}`, `nomarchy-theme-{bg-install,refresh,remove}`, `nomarchy-refresh-fastfetch`, `nomarchy-windows-vm` (new Virtualization section). Final state: 159 scripts, all `kept`, `unused?` = 0, missing references = 0.
- _2026-05-18_ — Pillar 3 Phase B: webapp/tui/voxtype install-remove pair triage. Deleted two dead webapp URI handlers (`nomarchy-webapp-handler-hey`, `nomarchy-webapp-handler-zoom`) — no `.desktop` MimeType registration anywhere routed `mailto:`/`zoom:` URIs to them, so the handlers could never fire. Surfaced six useful CLI tools in `SKILL.md` "Common Tasks" so they're discoverable by AI assistants and tagged `kept` by the audit: `nomarchy-webapp-{remove,remove-all}`, `nomarchy-tui-{remove,remove-all}`, `nomarchy-voxtype-{install,remove}`. Script count 166 → 164; `unused?` 21 → 13.
- _2026-05-18_ — Pillar 3 Phase B: dead-code sweep (NixOS-irrelevant Omarchy ports). Deleted five scripts that duplicated NixOS-native facilities or referenced infrastructure Nomarchy doesn't ship: `nomarchy-rollback` (boot-menu generations + `nixos-rebuild rollback` already cover this), `nomarchy-snapshot` (used `snapper`; impermanence and BTRFS subvolumes are the Nomarchy answer), `nomarchy-migrate-state` (one-shot pre-unification migration, no current callers), `nomarchy-config-direct-boot` (added an EFI entry for a UKI we never build), and `nomarchy-npx-install` (Arch idiom — `nix-shell -p nodejs` is the NixOS path). Kept `nomarchy-build-iso` and `nomarchy-build-live-iso` and surfaced them in README §2 so the audit tags them `kept`. Script count 171 → 166.
- _2026-05-18_ — Pillar 3 Phase B: missing-references triage. (1) Wrote `themes/engine/scripts/nomarchy-theme-next` so `SKILL.md`'s documented "cycle to next theme" command resolves; (2) scrubbed three stale `nomarchy-dev-*` references from `core/home/config/nomarchy-skill/SKILL.md`; (3) added a line-context filter to both `nomarchy-docs-scripts` generators that drops `nomarchy-*` tokens appearing in Nix `pname`/derivation idents, `/tmp/` & `/etc/sudoers.d/` paths, `nixosConfigurations.*` / `packages.*` flake outputs, `mktemp -t` prefixes, systemd unit vars, `./result/bin/run-` binaries, and `docker` container references; (4) added a small token-level denylist for five residual non-script identifiers (`nomarchy-plymouth`, `nomarchy-sddm-theme`, `nomarchy-live`, `nomarchy-rev`, `nomarchy-windows`) that survive line filtering. `docs/SCRIPTS.md` "Missing references" section is now empty (was 15).
- _2026-05-04_ — Pillar 8: Distro Branding. (1) Scrubbed remaining "Omarchy" and "Spirit of Omarchy" references from README, scripts, and welcome wizard; (2) Updated `nomarchy-welcome` banner and `nomarchy-version` codename ("Sovereign"); (3) Verified existing `core/system/branding.nix` handles OS-release and bootloader labels; (4) Confirmed SDDM and Plymouth metadata are already Nomarchy-branded.
- _2026-05-04_ — Thorough Out-of-the-Box QA Audit. (1) Restored automatic wallpaper switching by removing image filters from deployed themes; (2) Fixed broken "Style" menu entries by creating missing `about.txt` and `screensaver.txt` branding files; (3) Cleaned up conflicting keybindings by removing deprecated `tiling.conf` and updating the doc generator; (4) Removed legacy Nord theme hack from `nomarchy-theme-set`; (5) Fixed JSON parse error in `summer-day` waybar theme.
- _2026-05-03_ — Fixed multi-disk LUKS/BTRFS boot hang. (1) Moved temporary LUKS keyfile to `/tmp/` so Disko correctly omits it from the runtime configuration; (2) Injected `x-systemd.requires` and `x-systemd.device-timeout=0` into BTRFS mount options to ensure all LUKS drives are decrypted before mounting.
- _2026-05-03_ — Fixed CLI wrappers and removed obsolete code. (1) Updated `nomarchy-font`, `nomarchy-theme`, and `nomarchy-wallpaper` CLI wrappers to use modern Walker menus; (2) Removed the obsolete and broken `themes/engine/switcher.nix` and its associated Nix-inlined scripts; (3) Cleaned up remaining `$NOMARCHY_PATH` references from the Omarchy era.
- _2026-05-03_ — Fixed `/etc/nixos` ownership after installation. Added a `chown -R $USERNAME:users /etc/nixos` step via `nixos-enter` at the end of `installer/install.sh` so the main user owns their configuration and can run `home-manager` commands without `sudo`.
- _2026-05-01_ — Installer & Script Audit Polish. (1) Fixed a critical bash dynamic scoping bug in `installer/install.sh` where `rc=0` assignments inside functions (Impermanence, Form Factor) were clobbering the main loop's return code, causing the installer to abort when "No" was selected; (2) Polished `hosts/nomarchy-live.nix` with auto-login for the `nixos` user and passwordless sudo for the `wheel` group; (3) Repurposed `nomarchy-toggle-suspend` to execute `systemctl suspend` directly and updated `nomarchy-menu` to reflect this; (4) Updated `nomarchy-launch-wifi` to use `nmtui` in Alacritty; (5) Regenerated `docs/SCRIPTS.md` to reflect the updated script mappings.
- _2026-04-30_ — `set -e` sweep across `nomarchy-*` scripts. Added `set -e` to 142 of 169 bash scripts that lacked it (27 already had it). Halts a class of "command failed silently in the middle of a chain, system left in half-applied state" bugs that produced repeat-fix commits. One deliberate exception: `nomarchy-menu` runs without `set -e` because it's an interactive UX loop where action failures should re-display the menu rather than abort the script. Pre-commit hook now enforces `bash -n` + `shellcheck --severity=error` so future scripts can't regress this.
- _2026-04-30_ — Installer disk-phase reliability. Hardened `installer/install.sh` and consolidated the disko configs: (1) `select_disk` now hides the live-ISO boot device(s) so the installer can't format its own boot media (`NOMARCHY_INSTALL_ALLOW_ISO_TARGET=1` to override); (2) added a 10 GiB minimum-capacity preflight; (3) `prewipe_target_drive` enumerates every active dm-crypt mapping backed by the target drive and closes them, drops the silent `|| true` from `wipefs`/`sgdisk`/`dd`, bounds `udevadm settle` to 30s, and refuses to continue if anything is still mounted; (4) wrapped the disko call in `run_disko_with_retry` with last-30-lines + Retry / View full log / Abort dialog on failure; (5) replaced the sed-templated `disko-golden.nix` + `disko-btrfs-multi.nix` pair with a single `disko-config.nix` Nix function called via `--argstr mainDrive … --arg extraDrives '[…]'` — eliminates a class of escaping bugs (cf. `3aadc36`); (6) added an EXIT trap so the tmpfs LUKS key file is removed even on early abort.
- _2026-04-30_ — Gaming home-side companion. New `nomarchy.gaming.enable` option (mirror of `nomarchy.system.gaming.enable`) and `core/home/gaming.nix` module that injects a Hyprland `windowrulev2 = fullscreen, class:^(steam_app_).*$` so Steam-launched games grab the whole screen. Closes the "Gaming — Hyprland window rule" Next-column row.
- _2026-04-26_ — Default to highest resolution (`highres`) for monitors. Updated `features/desktop/hyprland/config/monitors.conf` and forced it in the live ISO (`nomarchy-live`) to resolve issues where some hardware would default to a low resolution (1024x768).
- _2026-04-26_ — First-run welcome wizard (`nomarchy-welcome`). Extended from a one-shot greeter into a guided picker for theme, font, and panel position. Added Step 4 to generate a starter `home.nix` if missing. State is now persisted in `state.json` via `.welcome_done`. Added `nomarchy.panelPosition` option to Waybar.
- _2026-04-26_ — Multi-disk BTRFS support in the installer. Added `installer/disko-btrfs-multi.nix` template and updated `installer/install.sh` to allow selecting multiple drives via `gum choose --no-limit`. Implements BTRFS "single" data + RAID1 metadata across multiple LUKS-encrypted drives.
- _2026-04-26_ — Distro Branding Phase 2. Updated bootloader entries to use "Nomarchy" as the label. Set ISO volume IDs to `NOMARCHY_INSTALLER` and `NOMARCHY_LIVE`. Fixed branding in Plymouth theme metadata and SDDM metadata.
- _2026-04-26_ — Distro Branding Phase 1. Renamed `installerIso` to `nomarchy-installer` and `installerIsoGraphical` to `nomarchy-live`. Updated metadata and host configurations. Scrubbed "Omarchy" from Plymouth and installer messages.
- _2026-04-26_ — Fix `hardware-db.sh` missing in `nomarchy-installer.nix`. Resolved boot error where `install.sh` failed to source the hardware database on the TTY installer ISO.
- _2026-04-26_ — Installer review-then-edit flow (`installer/install.sh`). Review screen now offers Continue / Edit a field / Abort. Edit opens a multi-select of saved fields; chosen fields clear and the next loop iteration re-prompts only those. Benefits both fresh installs (typo fixes without abort+restart) and `--resume` (lands on review immediately, since the loaded vars short-circuit each prompt). LUKS passphrase is held in memory across loop iterations so re-edits don't re-ask for it.
- _2026-04-26_ — `docs/TROUBLESHOOTING.md`. The five most common rebuild errors (option-already-declared, attribute-missing, Stylix target conflict, home-manager `.hm-bak` churn, impermanence path missing) with copy-paste fixes. Linked from `README.md` and `docs/MIGRATION.md`.
- _2026-04-26_ — Gaming preset module (`core/system/gaming.nix`). Opt-in `nomarchy.system.gaming.enable` (default false). Wires `programs.steam` (with `remotePlay`/`localNetworkGameTransfers` firewall holes via `mkDefault`), `programs.gamemode`, and `services.flatpak`. Flathub remote and Hyprland window-rule split into separate Next-column rows.
- _2026-04-26_ — Accessibility preset module (`core/system/accessibility.nix`). New `nomarchy.system.accessibility.{enable,cursorSize}` options (opt-in, default off — accessibility isn't a hardware-derived signal). Enables `services.gnome.at-spi2-core`, installs Orca, and sets `XCURSOR_SIZE=32` (configurable). Hyprland-side companion (key-repeat slowdown, Orca keybinding, high-contrast palette) split into a new Next-column row.
- _2026-04-26_ — Desktop preset module (`core/system/desktop.nix`). New `nomarchy.system.desktop.enable` option; defaults to `formFactor == "desktop"` (mirror of the laptop preset's auto-enable). Pins `powerManagement.cpuFreqGovernor` to `"performance"` and enables `services.zfs.{autoScrub,trim}` so a future ZFS pool gets sensible maintenance for free.
- _2026-04-26_ — Laptop preset module (`core/system/laptop.nix`). New `nomarchy.system.laptop.{enable,thermald}` options; `enable` defaults to `formFactor == "laptop"` so the installer's existing `formFactor` write auto-flips it on. Wires TLP (governors + 75/80 charge thresholds), force-disables `power-profiles-daemon`, enables `upower` and `thermald` (x86_64), adds the brightnessctl udev rule for backlight without root, and sets a logind lid-switch policy that defers to `hibernation.enable`. Closes both the Now item and the largest Next item.
- _2026-04-25_ — Software-profile multi-select in the installer. Users can now pick Dev, Gaming, Office, Media, and CLI Utils profiles during install; logic emits corresponding `home.packages` and system toggles into the generated config.
- _2026-04-25_ — Pillar 3 Phase B: script & menu audit. Ported/implemented/stubbed ~40 scripts including `nomarchy-version`, `nomarchy-debug`, `nomarchy-reinstall`, `nomarchy-rollback`, `nomarchy-update-firmware`, `nomarchy-pkg-*`, and `nomarchy-theme-*` wrappers. Moved desktop scripts to packaged utility directory.
- _2026-04-25_ — Docker & fwupd support. Added `nomarchy.system.virtualization.docker.enable` and `nomarchy.hardware.fwupd` options. Wires system services and adds `docker-compose` and `fwupdmgr` to PATH.
- _2026-04-25_ — Installer VM testing. Added `installerVm` to flake nixosConfigurations, packages, and apps. `nomarchy-test-installer` now uses `nix run .#installerVm`.
- _2026-04-25_ — `docs/KEYBINDINGS.md` auto-generator. New repo-tooling script `bin/utils/nomarchy-docs-keybindings` parses every `bindd =` / `bindeld =` line into a Markdown doc; README's keybinding table slimmed to highlights + link.
- _2026-04-25_ — Installer disk picker shows NAME / SIZE / TYPE / VENDOR / MODEL / SERIAL columns instead of bare `lsblk`. Type derived from `ROTA` + `TRAN` (NVMe / USB / SSD / HDD). Filters loop, ram, zram, sr.
- _2026-04-25_ — Pillar 3 Phase A: script & menu audit. New `bin/utils/nomarchy-docs-scripts` generator produces `docs/SCRIPTS.md` with 136 scripts and the menu walk pre-tagged via heuristics (`kept` / `unused?` / `missing`). Phase B (per-batch porting / removal) opens.
- _2026-04-25_ — Installer prompts for keyboard layout + locale, applies live; new `nomarchy.{system,}.formFactor` option; waybar drops battery widget on desktop; nm-applet visibility fix in default theme; live-ISO baseline keymap/locale (`a7e7fa9`).
- _2026-04-25_ — `docs/OPTIONS.md` reference; `docs/MIGRATION.md` linked from `README.md` (`3cb012b`, `6ef28f0`).

282
docs/SCRIPTS.md Normal file
View File

@@ -0,0 +1,282 @@
# 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.
## Scripts (159)
| Script | Location | Callers | Status | Notes |
| --- | --- | --- | --- | --- |
| `nomarchy-backup` | `features/scripts/utils` | features/scripts/utils/nomarchy-sync | `kept` | |
| `nomarchy-battery-capacity` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | |
| `nomarchy-battery-monitor` | `core/system/scripts` | features/scripts/battery-monitor.nix | `kept` | |
| `nomarchy-battery-remaining` | `core/system/scripts` | core/system/scripts/nomarchy-battery-monitor,core/system/scripts/nomarchy-battery-status | `kept` | |
| `nomarchy-battery-remaining-time` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | |
| `nomarchy-battery-status` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/system/scripts/nomarchy-battery-capacity, +1 more | `kept` | |
| `nomarchy-brightness-display` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | |
| `nomarchy-brightness-display-apple` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | |
| `nomarchy-brightness-keyboard` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | |
| `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-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/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +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-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-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-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-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` | |
| `nomarchy-hyprland-window-gaps-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hyprland-window-pop` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf | `kept` | |
| `nomarchy-hyprland-window-single-square-aspect-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | |
| `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` | core/home/config/nomarchy-skill/SKILL.md,hosts/nomarchy-installer.nix, +2 more | `kept` | |
| `nomarchy-install-docker-dbs` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `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-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-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-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-lock-screen` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/extensions/menu.sh, +3 more | `kept` | |
| `nomarchy-manual` | `features/scripts/utils` | core/branding/about.txt,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-menu` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +9 more | `kept` | |
| `nomarchy-menu-keybindings` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +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-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-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` | |
| `nomarchy-reinstall` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-app` | `features/scripts/utils` | core/system/scripts/nomarchy-restart-xcompose,features/scripts/utils/nomarchy-restart-hypridle, +3 more | `kept` | |
| `nomarchy-restart-bluetooth` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-btop` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | |
| `nomarchy-restart-hypridle` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-hyprsunset` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-opencode` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | |
| `nomarchy-restart-pipewire` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-swayosd` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `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-wifi` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-xcompose` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-setup-dns` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-setup-fido2` | `core/system/scripts` | features/scripts/utils/nomarchy-menu,installer/install.sh | `kept` | |
| `nomarchy-setup-fingerprint` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `kept` | |
| `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-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` | features/scripts/utils/nomarchy-backup,README.md | `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/extensions/menu.sh,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-sys-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts/nomarchy-setup-dns, +5 more | `kept` | |
| `nomarchy-test-installer` | `features/scripts/utils` | features/scripts/utils/nomarchy-test-vm,README.md | `kept` | |
| `nomarchy-test-live-iso` | `features/scripts/utils` | hosts/nomarchy-live.nix | `kept` | |
| `nomarchy-test-vm` | `features/scripts/utils` | features/scripts/utils/nomarchy-test-live-iso | `kept` | |
| `nomarchy-theme` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua, +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` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-wallpaper, +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-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/default/elephant/nomarchy_themes.lua,core/home/config/nomarchy-skill/SKILL.md, +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` | |
| `nomarchy-theme-set-obsidian` | `themes/engine/scripts` | themes/engine/scripts/nomarchy-theme-set | `kept` | |
| `nomarchy-theme-set-templates` | `themes/engine/scripts` | themes/engine/scripts/nomarchy-theme-set | `kept` | |
| `nomarchy-themes-prebuild` | `themes/engine/scripts` | installer/install.sh | `kept` | |
| `nomarchy-theme-update` | `themes/engine/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-toggle-hybrid-gpu` | `core/system/scripts` | features/scripts/utils/nomarchy-menu,features/scripts/utils/nomarchy-sys-update, +1 more | `kept` | |
| `nomarchy-toggle-idle` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +2 more | `kept` | |
| `nomarchy-toggle-nightlight` | `themes/engine/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-toggle-notification-silencing` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-toggle-screensaver` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-toggle-suspend` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-toggle-waybar` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-tui-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-tui-remove-all | `kept` | |
| `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/default/mako/core.ini,core/home/config/nomarchy-skill/SKILL.md, +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-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 | `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, +2 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,lib/state-schema.nix | `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` | |
## Missing references
Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bin/`, `lib/` that have no matching script file.
| Token | Referenced in | Status |
| --- | --- | --- |
## Menu items
Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*_menu` function becomes one row.
| Submenu | Entry | Calls | Status |
| --- | --- | --- | --- |
| `show_learn_menu` | Keybindings | `nomarchy-menu-keybindings` | `kept` |
| `show_learn_menu` | Nomarchy | `nomarchy-launch-webapp` | `kept` |
| `show_learn_menu` | Hyprland | `nomarchy-launch-webapp` | `kept` |
| `show_learn_menu` | Arch | `nomarchy-launch-webapp` | `kept` |
| `show_learn_menu` | Bash | `nomarchy-launch-webapp` | `kept` |
| `show_learn_menu` | Neovim | `nomarchy-launch-webapp` | `kept` |
| `show_trigger_menu` | Capture | `_(inline)_` | `kept` |
| `show_trigger_menu` | Share | `_(inline)_` | `kept` |
| `show_trigger_menu` | Toggle | `_(inline)_` | `kept` |
| `show_trigger_menu` | Hardware | `_(inline)_` | `kept` |
| `show_capture_menu` | Screenshot | `nomarchy-cmd-screenshot` | `kept` |
| `show_capture_menu` | Screenrecord | `_(inline)_` | `kept` |
| `show_capture_menu` | Color | `_(inline)_` | `kept` |
| `show_share_menu` | Clipboard | `nomarchy-cmd-share` | `kept` |
| `show_share_menu` | File | `nomarchy-cmd-share` | `kept` |
| `show_share_menu` | Folder | `nomarchy-cmd-share` | `kept` |
| `show_toggle_menu` | Screensaver | `nomarchy-toggle-screensaver` | `kept` |
| `show_toggle_menu` | Nightlight | `nomarchy-toggle-nightlight` | `kept` |
| `show_toggle_menu` | Idle | `nomarchy-toggle-idle` | `kept` |
| `show_toggle_menu` | Bar | `nomarchy-toggle-waybar` | `kept` |
| `show_toggle_menu` | Layout | `nomarchy-hyprland-workspace-layout-toggle` | `kept` |
| `show_toggle_menu` | Ratio | `nomarchy-hyprland-window-single-square-aspect-toggle` | `kept` |
| `show_toggle_menu` | Gaps | `nomarchy-hyprland-window-gaps-toggle` | `kept` |
| `show_toggle_menu` | Scaling | `nomarchy-hyprland-monitor-scaling-cycle` | `kept` |
| `show_style_menu` | Theme | `_(inline)_` | `kept` |
| `show_style_menu` | Font | `_(inline)_` | `kept` |
| `show_style_menu` | Background | `_(inline)_` | `kept` |
| `show_style_menu` | Hyprland | `_(inline)_` | `kept` |
| `show_style_menu` | Screensaver | `_(inline)_` | `kept` |
| `show_style_menu` | About | `_(inline)_` | `kept` |
| `show_setup_menu` | Audio | `nomarchy-launch-audio` | `kept` |
| `show_setup_menu` | Wifi | `nomarchy-launch-wifi` | `kept` |
| `show_setup_menu` | Bluetooth | `nomarchy-launch-bluetooth` | `kept` |
| `show_setup_menu` | Power | `_(inline)_` | `kept` |
| `show_setup_menu` | System | `_(inline)_` | `kept` |
| `show_setup_menu` | Monitors | `_(inline)_` | `kept` |
| `show_setup_menu` | Keybindings | `_(inline)_` | `kept` |
| `show_setup_menu` | Input | `_(inline)_` | `kept` |
| `show_setup_menu` | DNS | `nomarchy-setup-dns` | `kept` |
| `show_setup_menu` | Security | `_(inline)_` | `kept` |
| `show_setup_menu` | Config | `_(inline)_` | `kept` |
| `show_setup_security_menu` | Fingerprint | `nomarchy-setup-fingerprint` | `kept` |
| `show_setup_security_menu` | Fido2 | `nomarchy-setup-fido2` | `kept` |
| `show_setup_config_menu` | Defaults | `_(inline)_` | `kept` |
| `show_setup_config_menu` | Hyprland | `_(inline)_` | `kept` |
| `show_setup_config_menu` | Hypridle | `nomarchy-restart-hypridle` | `kept` |
| `show_setup_config_menu` | Hyprlock | `_(inline)_` | `kept` |
| `show_setup_config_menu` | Hyprsunset | `nomarchy-restart-hyprsunset` | `kept` |
| `show_setup_config_menu` | Swayosd | `nomarchy-restart-swayosd` | `kept` |
| `show_setup_config_menu` | Walker | `nomarchy-restart-walker` | `kept` |
| `show_setup_config_menu` | Waybar | `nomarchy-restart-waybar` | `kept` |
| `show_setup_config_menu` | XCompose | `nomarchy-restart-xcompose` | `kept` |
| `show_setup_config_menu` | Overrides | `_(inline)_` | `kept` |
| `show_update_menu` | Nomarchy | `nomarchy-update` | `kept` |
| `show_update_menu` | Themes | `nomarchy-theme-update` | `kept` |
| `show_update_menu` | Process | `_(inline)_` | `kept` |
| `show_update_menu` | Hardware | `_(inline)_` | `kept` |
| `show_update_menu` | Firmware | `nomarchy-update-firmware` | `kept` |
| `show_update_menu` | Timezone | `nomarchy-tz-select` | `kept` |
| `show_update_menu` | Time | `nomarchy-update-time` | `kept` |
| `show_update_menu` | Password | `_(inline)_` | `kept` |
| `show_update_process_menu` | Hypridle | `nomarchy-restart-hypridle` | `kept` |
| `show_update_process_menu` | Hyprsunset | `nomarchy-restart-hyprsunset` | `kept` |
| `show_update_process_menu` | Swayosd | `nomarchy-restart-swayosd` | `kept` |
| `show_update_process_menu` | Walker | `nomarchy-restart-walker` | `kept` |
| `show_update_process_menu` | Waybar | `nomarchy-restart-waybar` | `kept` |
| `show_update_hardware_menu` | Audio | `nomarchy-restart-pipewire` | `kept` |
| `show_update_hardware_menu` | Wi-Fi | `nomarchy-restart-wifi` | `kept` |
| `show_update_hardware_menu` | Bluetooth | `nomarchy-restart-bluetooth` | `kept` |
| `show_update_password_menu` | Drive | `nomarchy-drive-set-password` | `kept` |
| `show_update_password_menu` | User | `_(inline)_` | `kept` |
| `show_system_menu` | Screensaver | `nomarchy-launch-screensaver` | `kept` |
| `show_system_menu` | Lock | `nomarchy-lock-screen` | `kept` |
| `show_system_menu` | Suspend | `nomarchy-toggle-suspend` | `kept` |
| `show_system_menu` | Hibernate | `_(inline)_` | `kept` |
| `show_system_menu` | Logout | `nomarchy-system-logout` | `kept` |
| `show_system_menu` | Restart | `nomarchy-system-reboot` | `kept` |
| `show_system_menu` | Shutdown | `nomarchy-system-shutdown` | `kept` |

Some files were not shown because too many files have changed in this diff Show More