Compare commits

...

104 Commits

Author SHA1 Message Date
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
272 changed files with 8702 additions and 2359 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** 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
@@ -40,7 +40,8 @@ This builds a full graphical VM of the installer environment. Once inside, click
### 2. Build the Installer ISO
To install on physical hardware, generate your own bootable image:
```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.
@@ -55,6 +56,9 @@ The wizard will guide you through:
- **Storage:** Choice between Standard Ext4 or Encrypted BTRFS with optional **Impermanence**.
- **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
@@ -80,6 +84,8 @@ Add user-level packages, aliases, and dotfiles here.
nomarchy.home.terminal = "kitty";
```
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). Hit a rebuild error? Check [Troubleshooting](docs/TROUBLESHOOTING.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
### 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:
@@ -92,12 +98,12 @@ env-update # Reloads your Home Manager environment (Runs: home-manager switch -
## 🚀 Commands & Keybindings
The full list lives in [`docs/KEYBINDINGS.md`](docs/KEYBINDINGS.md) (auto-generated from the Hyprland configs). A few highlights:
| Keybinding | Action |
| :--- | :--- |
| `Super + Space` | **App Launcher** (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 + Return` | Open Terminal |
| `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.
---
*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,
wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace
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
@@ -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.**
**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
@@ -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-bg-next # Cycle wallpaper
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
@@ -311,10 +314,48 @@ nomarchy-debug --no-sudo --print # Debug info (ALWAYS use these flags)
nomarchy-lock-screen # Lock screen
nomarchy-system-shutdown # Shutdown
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.
### 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
```bash
@@ -325,6 +366,7 @@ nomarchy-debug --no-sudo --print
nomarchy-upload-log
# Reset specific config to defaults
# Examples: nomarchy-refresh-fastfetch, nomarchy-refresh-hyprland, nomarchy-refresh-waybar
nomarchy-refresh-<app>
# 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:
- Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.)
- Creating or editing migrations
- Running `nomarchy-dev-*` commands
- Modifying Nomarchy's own source tree
## Example Requests

View File

@@ -3,5 +3,4 @@ export SUDO_EDITOR="$EDITOR"
export BAT_THEME=ansi
# Duplicated from .config/uwsm/env so SSH works too
export NOMARCHY_PATH=$HOME/.local/share/nomarchy
export PATH=$NOMARCHY_PATH/bin:$PATH:$HOME/.local/bin
export PATH=$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()
local entries = {}
local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes"
local nomarchy_path = os.getenv("NOMARCHY_PATH") or ""
local default_theme_dir = nomarchy_path .. "/themes"
local default_theme_dir = os.getenv("HOME") .. "/.local/share/nomarchy/themes"
local seen_themes = {}

View File

@@ -1,7 +1,9 @@
exec-once = uwsm-app -- hypridle
exec-once = uwsm-app -- mako
exec-once = nomarchy-welcome
# 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)
# so failures surface in logs and the wallpaper survives Hyprland restarts.
exec-once = uwsm-app -- swayosd-server
@@ -10,3 +12,6 @@ exec-once = nomarchy-on-boot
# Slow app launch fix -- set systemd vars
exec-once = systemctl --user import-environment $(env | cut -d'=' -f 1)
exec-once = dbus-update-activation-environment --systemd --all
# Network Manager
exec-once = uwsm-app 'nm-applet --indicator'

View File

@@ -12,5 +12,5 @@ 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/tiling-v2.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,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
configItems = {
# Directories
"environment.d" = "directory";
fastfetch = "directory";
fcitx5 = "directory";
fontconfig = "directory";
@@ -16,10 +15,8 @@ let
"nautilus-python" = "directory";
nomarchy = "directory";
"nomarchy-skill" = "directory";
Typora = "directory";
uwsm = "directory";
wiremix = "directory";
xournalpp = "directory";
# Files
"brave-flags.conf" = "regular";
@@ -54,5 +51,17 @@ let
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
./state.nix
./overrides.nix
./behavior.nix
./fonts.nix
./configs.nix
./security.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, ... }:
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 = {
toggles = {
suspend = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.suspend;
description = "Whether to show suspend in system menu.";
};
screensaver = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.screensaver;
description = "Whether the screensaver is enabled.";
};
idle = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.idle;
description = "Whether the idle lock is enabled.";
};
nightlight = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.home.nightlight;
description = "Whether the nightlight is enabled.";
};
waybar = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.waybar;
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 {
type = lib.types.int;
default = 4000;
default = schema.home.nightlightTemperature;
description = "Temperature for the nightlight.";
};
theme = lib.mkOption {
type = lib.types.str;
default = "summer-night";
default = schema.home.theme;
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 {
type = lib.types.str;
default = "";
default = schema.home.wallpaper;
description = "System wallpaper path.";
};
panelPosition = lib.mkOption {
type = lib.types.enum [ "top" "bottom" ];
default = schema.home.panelPosition;
description = "Waybar panel position.";
};
hyprland = {
gaps_in = lib.mkOption {
type = lib.types.int;
default = 5;
default = schema.home.hyprland.gaps_in;
description = "Inner gaps for Hyprland.";
};
gaps_out = lib.mkOption {
type = lib.types.int;
default = 10;
default = schema.home.hyprland.gaps_out;
description = "Outer gaps for Hyprland.";
};
border_size = lib.mkOption {
type = lib.types.int;
default = 2;
default = schema.home.hyprland.border_size;
description = "Border size for Hyprland.";
};
};
fonts = {
monospace = lib.mkOption {
type = lib.types.str;
default = "JetBrainsMono Nerd Font";
default = schema.home.font;
description = "System monospace font.";
};
};
@@ -100,5 +117,27 @@
default = null;
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, ... }:
# File-based override system for Nomarchy
# File-based override system for Nomarchy.
#
# Users can place config files in ~/.config/nomarchy/overrides/ to completely
# replace upstream defaults. Override priority (highest to lowest):
# 1. User Nix options
# 2. User file overrides (~/.config/nomarchy/overrides/)
# 3. Upstream defaults
# STATUS: option surface only — the actual override mechanism is NOT yet
# implemented. The options are kept so configs that already set
# `nomarchy.overrides.enable = …;` continue to evaluate; setting them has
# no effect today. Tracked in docs/ROADMAP.md.
#
# Supported override paths:
# - hypr/ - Hyprland configs (bindings.conf, input.conf, etc.)
# - waybar/ - Waybar config and style
# - alacritty/ - Alacritty terminal config
# - walker/ - Walker launcher config
# - kitty/ - Kitty terminal config
# - btop/ - Btop resource monitor config
# - apps/ - Other application configs
# When implemented, this module should substitute sources in
# `xdg.configFile.<path>.source` based on the presence of matching files
# under ~/.config/nomarchy/overrides/.
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 = {
enable = lib.mkOption {
type = lib.types.bool;
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 {
type = lib.types.attrsOf lib.types.path;
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
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;
# Read unified state from ~/.config/nomarchy/state.json
togglesState = nomarchyLib.readHomeState config.home.homeDirectory;
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 = {
nomarchy = {
toggles = {
suspend = togglesState.suspend or true;
screensaver = togglesState.screensaver or true;
idle = togglesState.idle or true;
nightlight = togglesState.nightlight or false;
waybar = togglesState.waybar or true;
skipVsCodeTheme = togglesState.skipVsCodeTheme or false;
suspend = lib.mkDefault (togglesState.suspend or schema.home.suspend);
screensaver = lib.mkDefault (togglesState.screensaver or schema.home.screensaver);
idle = lib.mkDefault (togglesState.idle or schema.home.idle);
nightlight = lib.mkDefault (togglesState.nightlight or schema.home.nightlight);
waybar = lib.mkDefault (togglesState.waybar or schema.home.waybar);
};
nightlightTemperature = togglesState.nightlightTemperature or 4000;
theme = togglesState.theme or "summer-night";
wallpaper = togglesState.wallpaper or "";
nightlightTemperature = lib.mkDefault (togglesState.nightlightTemperature or schema.home.nightlightTemperature);
theme = lib.mkDefault (togglesState.theme or schema.home.theme);
wallpaper = lib.mkDefault (togglesState.wallpaper or schema.home.wallpaper);
panelPosition = lib.mkDefault (togglesState.panelPosition or schema.home.panelPosition);
hyprland = {
gaps_in = togglesState.hyprland.gaps_in or 5;
gaps_out = togglesState.hyprland.gaps_out or 10;
border_size = togglesState.hyprland.border_size or 2;
gaps_in = lib.mkDefault (togglesState.hyprland.gaps_in or schema.home.hyprland.gaps_in);
gaps_out = lib.mkDefault (togglesState.hyprland.gaps_out or schema.home.hyprland.gaps_out);
border_size = lib.mkDefault (togglesState.hyprland.border_size or schema.home.hyprland.border_size);
};
fonts.monospace = togglesState.font or "JetBrainsMono Nerd Font";
fonts.monospace = lib.mkDefault (togglesState.font or schema.home.font);
# Derived properties from the theme directory
isLightMode = nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or "summer-night";
isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or schema.home.theme;
inherit assetsPath;
};
iconsTheme = nomarchyLib.getIconsTheme {
themeName = togglesState.theme or "summer-night";
});
iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme {
themeName = togglesState.theme or schema.home.theme;
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.
@@ -8,4 +8,10 @@
distroId = "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
./impermanence.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/sddm.nix
];
time.timeZone = lib.mkDefault config.nomarchy.system.timezone;
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
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";
version = "1.0";
# Point directly to the font file
src = ./../home/config/Nomarchy.ttf;
src = ./../branding/Nomarchy.ttf;
# No archive to unpack
unpackPhase = "true";
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";
isFramework = mkEnableOption "Framework laptop specific hardware fixes";
hasIPU7Camera = mkEnableOption "Intel IPU7 camera support";
fwupd = mkOption {
type = types.bool;
default = false;
description = "Whether to enable fwupd firmware update service.";
};
};
config = mkMerge [
(mkIf cfg.fwupd {
services.fwupd.enable = true;
})
(mkIf cfg.isXPS {
services.udev.extraRules = ''
ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:00:19.0", ATTR{power/control}="on"
@@ -61,8 +70,47 @@ in
})
(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;
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 = {
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 {
# 1. The Rollback Script: Runs in initrd before filesystems are mounted
boot.initrd.postDeviceCommands = lib.mkAfter ''
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
mkdir -p /btrfs_tmp/old_roots
@@ -58,7 +84,7 @@ in
"/etc/machine-id"
"/etc/supergfxd.conf"
];
users.nomarchy = {
users.${cfg.user} = {
directories = [
".ssh"
".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
'';
};
environment.systemPackages = [
pkgs.networkmanagerapplet
];
}

View File

@@ -11,6 +11,14 @@
# Optimize storage by hard-linking identical files
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.

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 = {
dns = lib.mkOption {
type = lib.types.enum [ "Cloudflare" "Google" "DHCP" "Custom" ];
default = "DHCP";
default = schema.system.dns;
description = "Selected DNS provider.";
};
customDns = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
default = schema.system.customDns;
description = "List of custom DNS servers.";
};
wifi = {
powersave = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.system.wifi.powersave;
description = "Whether to enable wifi power saving.";
};
};
timezone = lib.mkOption {
type = lib.types.str;
default = "UTC";
default = schema.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 = {
fingerprint = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.system.features.fingerprint;
description = "Whether to enable fingerprint support.";
};
fido2 = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.system.features.fido2;
description = "Whether to enable FIDO2 support.";
};
hybridGPU = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.system.features.hybridGPU;
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 {
type = lib.types.str;
default = "summer-night";
default = schema.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
supergfxctl
systemd
fwupd
hyprland
swayosd
];
in
pkgs.stdenv.mkDerivation {
@@ -52,8 +55,7 @@ pkgs.stdenv.mkDerivation {
for file in $out/bin/*; do
if [ -f "$file" ]; then
wrapProgram "$file" \
--prefix PATH : "$deps" \
--set NOMARCHY_PATH "/etc/nixos/nomarchy"
--prefix PATH : "$deps"
fi
done
'';

View File

@@ -2,4 +2,13 @@
{
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
set -e
# Returns the battery full capacity in Wh (rounded to whole number).
# Used by nomarchy-battery-status for displaying battery capacity.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# 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.

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# 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
set -e
# Match against the computer's DMI product name (case-insensitive).
# 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
set -e
# Detect whether Vulkan is available.

View File

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

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# 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
set -e
# Restart the PipeWire audio service to fix audio issues or apply new configuration.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash
set -e
# 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
set -e
# Toggle passwordless sudo for the current user.
# First run: enables passwordless sudo for 15 minutes (after confirmation).

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# 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.

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# 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.

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# 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.

View File

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

View File

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

View File

@@ -1,29 +1,5 @@
#!/usr/bin/env bash
set -e
# Toggles the suspend menu option availability.
# Hybrid: updates state.json and runs env-update for persistence.
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
# Execute system suspend
systemctl suspend

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash
set -e
# Toggles wifi power saving declaratively.
# 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
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;
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 = {
dns = systemState.dns or "DHCP";
customDns = systemState.customDns or [];
wifi.powersave = systemState.wifi.powersave or true;
timezone = systemState.timezone or "UTC";
dns = lib.mkDefault (systemState.dns or schema.system.dns);
customDns = lib.mkDefault (systemState.customDns or schema.system.customDns);
wifi.powersave = lib.mkDefault (systemState.wifi.powersave or schema.system.wifi.powersave);
timezone = lib.mkDefault (systemState.timezone or schema.system.timezone);
features = {
fingerprint = systemState.features.fingerprint or false;
fido2 = systemState.features.fido2 or false;
hybridGPU = systemState.features.hybridGPU or false;
makima = systemState.features.makima or false;
fingerprint = lib.mkDefault (systemState.features.fingerprint or schema.system.features.fingerprint);
fido2 = lib.mkDefault (systemState.features.fido2 or schema.system.features.fido2);
hybridGPU = lib.mkDefault (systemState.features.hybridGPU or schema.system.features.hybridGPU);
};
theme = 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 = {
enable = lib.mkDefault true;
waylandCompositors.hyprland = {
@@ -8,4 +14,23 @@
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.*`

201
docs/ROADMAP.md Normal file
View File

@@ -0,0 +1,201 @@
# 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)
- **Full QA audit of shipped features.** Walk every feature/component end-to-end on a real install, fix what's small, log what's not. Runs as per-component PR sweeps — methodology in [Pillar 8](#8-pillar-qa-audit--features--components).
- **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).
### 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.
- **`themes/templates/*.tpl` — decide what these are for.** Eleven mustache-style templates (`alacritty.toml.tpl`, `btop.theme.tpl`, `kitty.conf.tpl`, etc.) are deployed to `~/.local/share/nomarchy/templates/` via `themes/engine/files.nix`, but no script in the tree consumes them. Likely vestigial from a pre-stylix templating system. Either delete the directory + the `xdg.dataFile` deployment, or document them as user-reference assets and explain how to use them.
- **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.
## 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-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` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,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` | `core/system/scripts` | 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/default/hypr/plain-bindings.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,features/desktop/hyprland/config/bindings.conf, +2 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` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,features/desktop/hyprland/config/bindings.conf, +5 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, +10 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` | `core/system/scripts` | 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` | `core/system/scripts` | features/scripts/utils/nomarchy-pkg-drop | `kept` | |
| `nomarchy-powerprofiles-list` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-preflight-migration` | `core/system/scripts` | core/system/scripts/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 | `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` |

View File

@@ -31,12 +31,21 @@ While the system is defined declaratively, Nomarchy uses a small, local state fi
- **Outputs:**
- `nixosModules.system`: Exports the foundational OS logic (`./core`).
- `nixosModules.home`: Exports the application and desktop logic (`./features`).
- `nixosConfigurations`: Defines pre-configured targets like `installerIso`, `live-iso`, and a testing `vm`.
- `nixosConfigurations`: Defines pre-configured targets like `nomarchy-installer`, `nomarchy-live`, and a testing `vm`.
- **`flake.lock`**: Locks dependency versions for reproducible builds.
- **`GEMINI.md`**: Foundational mandates and architectural rules for the Nomarchy Agent.
- **`STRUCTURE.md`**: (This file) Detailed architectural documentation.
- **`README.md`**: Project overview, installation instructions, and basic usage.
- **`TODO.md`**: Roadmap and pending tasks.
- **`docs/`**: All long-form documentation. Key entry points:
- **`AGENT.md`**: Onboarding for AI coding agents picking up Nomarchy.
- **`STRUCTURE.md`**: (This file) Detailed architectural documentation.
- **`OPTIONS.md`**: Reference for every `nomarchy.*` option.
- **`ROADMAP.md`**: Now / Next / Later board and the Shipped log.
- **`MIGRATION.md`**: Layering Nomarchy onto an existing NixOS install.
- **`KEYBINDINGS.md`**: Auto-generated keybinding reference.
- **`SCRIPTS.md`**: Auto-generated `nomarchy-*` script audit.
- **`TROUBLESHOOTING.md`**: Common rebuild errors and fixes.
- **`creating-themes.md`**: Theme palette authoring guide.
- **`.forgejo/workflows/`**: Forgejo Actions CI. Runs `nix flake check --no-build`, lints every `nomarchy-*` bash script with `bash -n` + `shellcheck --severity=error`, and verifies `docs/SCRIPTS.md` is up to date on every push to `main` and every PR. To activate: enable Actions on the repo in Forgejo and register a `forgejo-runner` (any Docker-capable Linux host works; the workflow uses `ubuntu-latest` and installs Nix itself).
- **`.githooks/`**: Optional per-clone git hooks (`pre-commit` lints changed scripts and regenerates `docs/SCRIPTS.md`). Enable with `git config core.hooksPath .githooks`. CI enforces the same invariants tree-wide.
---
@@ -61,8 +70,8 @@ The `core/` directory contains the foundational modules required for a functiona
- **`default.nix`**: The entry point for the base Home Manager module.
- **`options.nix`**: Defines the `nomarchy` user options (Toggles, Theme, Fonts, etc.).
- **`state.nix`**: Loads and applies user-level state (from `~/.config/nomarchy/state.json`).
- **`behavior.nix`**: Deploys non-visual configs (Keybindings, Input settings, Window rules) with `lib.mkDefault`.
- **`configs.nix`**: Manages static configuration files and directories in `~/.config/`.
- **`overrides.nix`**: Declares `nomarchy.overrides.*` (reserved for a future file-based override loader; currently no-op).
- **`configs.nix`**: Manages static configuration files and directories in `~/.config/`. Honors `nomarchy.configOverrides` as a bulk redirect to a replacement config dir.
- **`bash.nix`**: Shell environment, aliases, and specialized `env-update` hooks.
- **`security.nix`**: Polkit, keyring management, and GPG settings.
- **`config/`**: Contains the physical source files for the base user configuration (e.g., `starship.toml`, `hypr/` behavior configs).
@@ -93,7 +102,6 @@ The theming system is the "soul" of Nomarchy, providing dynamic, consistent aest
### `themes/engine/` (The Logic)
- **`loader.nix`**: The core theme loader. It reads the active theme from state and selectively deploys app-specific themed configs (e.g., `btop.theme`, `kitty.conf`) to `~/.config/`.
- **`stylix.nix`**: Integration with Stylix for base color palette and wallpaper management.
- **`switcher.nix`**: Provides the `nomarchy-theme-selector` and `nomarchy-wallpaper-selector` tools.
- **`plymouth.nix`** & **`sddm.nix`**: System-level theming for the boot screen and login manager.
### `themes/palettes/` (The Data)
@@ -125,9 +133,8 @@ The `lib/` directory provides centralized logic and data structures to maintain
### `installer/` (Bootstrap)
- **`install.sh`**: The interactive TTY-based installer. It handles disk partitioning, NixOS installation, and generating a clean "Downstream" flake for the user.
- **`disko-golden.nix`**: The standard partition layout (BTRFS on top of LUKS2).
- **`disko-btrfs-luks.nix`**: A simpler reference layout for disk management.
- **`disko-config.nix`**: The disko partition layout (BTRFS on top of LUKS2). A Nix function of `{ mainDrive, extraDrives ? [] }` — single-disk path is `extraDrives = []`; multi-disk adds BTRFS `-d single -m raid1` across the extras. Invoked by `install.sh` via `disko --argstr mainDrive … --arg extraDrives '[…]'`.
### `hosts/` (Targets)
- **`installer-iso.nix`**: Configuration for the minimal, TTY-based installation ISO.
- **`live-iso.nix`**: Configuration for the full graphical live environment, used for testing and GUI-based installation.
- **`nomarchy-installer.nix`**: Configuration for the minimal, TTY-based installation ISO.
- **`nomarchy-live.nix`**: Configuration for the full graphical live environment, used for testing and GUI-based installation.

121
docs/TROUBLESHOOTING.md Normal file
View File

@@ -0,0 +1,121 @@
# Troubleshooting
The five rebuild errors a Nomarchy user is most likely to hit, with copy-paste fixes. If your error isn't here, the [Options Reference](OPTIONS.md) and [Migration Guide](MIGRATION.md) cover most other surfaces.
---
## 1. `error: The option 'X' is already declared in multiple modules`
**Looks like:**
```
error: The option `services.foo.enable' is already declared in multiple modules:
- /nix/store/…-source/modules/foo.nix
- /nix/store/…-source/core/system/foo.nix
```
**Cause:** Two modules — usually one of yours and one of Nomarchy's — both `mkOption` the same path. Nix can't merge two declarations of the *same option*; it can only merge two *values* for one option.
**Fix:** Find the duplicate. If it's yours and Nomarchy's, delete yours; Nomarchy already declares it. If it's two of yours, delete one. To grep:
```bash
grep -rn "options\..*foo\.enable" /etc/nixos
```
---
## 2. `error: attribute 'X' missing`
**Looks like:**
```
error: attribute 'nomarchy' missing
at /etc/nixos/system.nix:7:5:
7| nomarchy.system.formFactor = "laptop";
```
**Cause:** You're setting `nomarchy.*` options but didn't import the Nomarchy modules. The flake's `nixosModules.system` declares the option namespace; without the import, the option doesn't exist.
**Fix:** In your `/etc/nixos/flake.nix`, make sure both modules are in the system's `modules = [ … ]` list:
```nix
modules = [
nomarchy.nixosModules.system
./system.nix
./hardware-selection.nix
];
```
The home side is the same shape — `nomarchy.nixosModules.home` plus your `./home.nix`. See [MIGRATION.md](MIGRATION.md) for the full skeleton.
---
## 3. Stylix target conflict
**Looks like:**
```
error: The option `stylix.targets.gtk.enable' has conflicting definition values:
- In `/nix/store/…/stylix.nix': true
- In `/etc/nixos/home.nix': false
Use `lib.mkForce' or `lib.mkDefault' to resolve.
```
**Cause:** Stylix's per-target options are `bool`, not `enum`, so two equally-priority `true`/`false` values from two modules collide. Nomarchy's theming engine sets most targets to `true` via `mkDefault`, so the conflict almost always means *you* set one without `mkDefault`.
**Fix:** Wrap your override in `lib.mkForce`:
```nix
stylix.targets.gtk.enable = lib.mkForce false;
```
If you want the default-on behavior back, just delete your line — Nomarchy's default fires automatically.
---
## 4. home-manager `backupFileExtension` churn
**Looks like:** every rebuild leaves another `~/.config/foo/bar.conf.hm-bak` next to the file home-manager just wrote, until your `~/.config` is half backups.
**Cause:** home-manager refuses to overwrite files it didn't itself write; it backs them up first. Nomarchy's flake sets `backupFileExtension = "hm-bak"` (see `flake.nix:161,210`) so the first rebuild after a fresh ISO install doesn't fail — but every subsequent rebuild then re-backs-up the same files because the previous backup is still there.
**Fix:** After the first successful rebuild, delete the backups:
```bash
find ~/.config -name '*.hm-bak' -print -delete
```
If churn continues, you have a config under `~/.config/<app>/` that home-manager wants to manage but you've also touched by hand. Either let home-manager own it (don't edit by hand; use `nomarchy.*` options or `~/.config/nomarchy/overrides/`) or delete the home-manager declaration if you want the file to remain user-mutable.
---
## 5. impermanence path missing after a wipe
**Looks like:** after enabling `nomarchy.system.impermanence.enable = true;` and rebooting, an app forgets state — Bluetooth pairings vanish, NetworkManager forgets Wi-Fi, GPG keys are gone — or the rebuild itself errors with:
```
error: The path '/persist' does not exist
```
**Cause:** Impermanence requires (a) a `/persist` mountpoint that survives the boot wipe, and (b) every directory you want to keep must be in the persistence list. Nomarchy persists the basics in `core/system/impermanence.nix:46-72` (NetworkManager, Bluetooth, fprint, SSH host keys, the user's `.ssh` / `.gnupg` / Documents / Downloads / Pictures / Videos / Projects). Anything else you care about — Steam library, Flatpak data, custom dotfiles — must be added.
**Fix:** Make sure `/persist` is mounted (check `mount | grep persist`). Then add the missing path in your `system.nix`:
```nix
environment.persistence."/persist".users.nomarchy.directories = [
".local/share/Steam"
".var/app" # flatpak data
".local/share/keyrings" # already in Nomarchy defaults — example only
];
```
Per-app data lives in `~/.local/share/<app>` or `~/.var/app/<id>` (flatpak); check the app's docs. After adding, rebuild and reboot — the path is created on the next mount of `/persist`.
---
## Where to look next
- **Option reference:** [docs/OPTIONS.md](OPTIONS.md) — every `nomarchy.*` setting.
- **Existing-NixOS install:** [docs/MIGRATION.md](MIGRATION.md) — how to layer Nomarchy onto a working NixOS without reformatting.
- **Repo layout:** [docs/STRUCTURE.md](STRUCTURE.md) — where each module lives.
- **Roadmap:** [docs/ROADMAP.md](ROADMAP.md) — what's planned and what's shipped.

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