Compare commits

..

39 Commits

Author SHA1 Message Date
Bernardo Magri
4de8afbea9 docs(roadmap): close out Pillar 8 / Component 6 + log VSCode marketplace gap
Component 6 (Apps) closeout entry covers the three inline theming
fixes (kitty/ghostty per-palette colors, btop color_theme name match,
VSCode theme extensions split). One new Later row tracks the 15
palettes — including the default summer-night (sainnhe.everforest) —
whose theme extensions aren't in nixpkgs and need
pkgs.vscode-utils.extensionFromVscodeMarketplace per-palette pinning.

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

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

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

Renamed the config reference to match the deployed file name.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Added:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

No code touched; doc-only.

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

Before:

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

After:

  nix eval --impure --raw --expr "
    let
      flake = builtins.getFlake \"$NOMARCHY_REPO\";
      lib = flake.inputs.nixpkgs.lib;
      schema = import \"$NOMARCHY_REPO/lib/state-schema.nix\"
                 { inherit lib; };
      state = schema.system // { timezone = \"$_state_tz\"; };
    in builtins.toJSON state
  " | nrun jq '.' > /mnt/etc/nixos/state.json

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This change:

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

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

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

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

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 17:34:05 +01:00
61 changed files with 707 additions and 742 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

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,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 "/etc/nixos"
local default_theme_dir = nomarchy_path .. "/themes/palettes"
local default_theme_dir = os.getenv("HOME") .. "/.local/share/nomarchy/themes"
local seen_themes = {}

View File

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

View File

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

View File

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

View File

@@ -51,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,7 +5,6 @@
./options.nix
./state.nix
./overrides.nix
./behavior.nix
./fonts.nix
./configs.nix
./security.nix

View File

@@ -1,47 +1,47 @@
{ 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 {
@@ -58,35 +58,35 @@
};
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 = "top";
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.";
};
};

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,42 +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 "nord";
wallpaper = togglesState.wallpaper or "";
panelPosition = togglesState.panelPosition or "top";
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 "nord";
isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or schema.home.theme;
inherit assetsPath;
};
iconsTheme = nomarchyLib.getIconsTheme {
themeName = togglesState.theme or "nord";
});
iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme {
themeName = togglesState.theme or schema.home.theme;
inherit assetsPath;
};
});
};
};
}

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 = ''

View File

@@ -70,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.
})
];
}

View File

@@ -15,9 +15,9 @@ in
settings.Login = {
HandleLidSwitch = lib.mkDefault "suspend-then-hibernate";
HandleLidSwitchExternalPower = lib.mkDefault "suspend";
HandlePowerKey = "hibernate";
IdleAction = "suspend-then-hibernate";
IdleActionSec = toString (cfg.idleMinutes * 60);
HandlePowerKey = lib.mkDefault "hibernate";
IdleAction = lib.mkDefault "suspend-then-hibernate";
IdleActionSec = lib.mkDefault (toString (cfg.idleMinutes * 60));
};
};
};

View File

@@ -24,6 +24,18 @@ in
"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 {
@@ -72,7 +84,7 @@ in
"/etc/machine-id"
"/etc/supergfxd.conf"
];
users.nomarchy = {
users.${cfg.user} = {
directories = [
".ssh"
".gnupg"

View File

@@ -1,27 +1,32 @@
{ 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 {
@@ -39,24 +44,24 @@
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).";
};
};
theme = lib.mkOption {
type = lib.types.str;
default = "nord";
description = "Selected system theme. Matches lib/state-schema.nix and the installer-written state.json so a missing/blank state file lands on the same theme everywhere.";
default = schema.system.theme;
description = "Selected system theme.";
};
# ----- Tier 1 system features (all opt-in, no behavioural change off) ---

View File

@@ -55,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"
--prefix PATH : "$deps"
fi
done
'';

View File

@@ -2,19 +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;
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

@@ -41,8 +41,7 @@ core/ Foundational OS + user defaults. Don't put apps here.
scripts/ Low-level system scripts (battery, brightness, hardware).
home/ Home Manager modules.
options.nix Most home-side nomarchy.* options.
behavior.nix nomarchy.behavior.* (deploy-default-config toggles).
overrides.nix File-based overrides from ~/.config/nomarchy/overrides/.
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.

View File

@@ -210,7 +210,7 @@ and it's whichever has higher Nix priority. Fix these explicitly:
| 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 = true; user = "nomarchy";` (mkDefault) | Override with `services.displayManager.autoLogin.user = "<your user>"` or disable |
| 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

View File

@@ -53,7 +53,25 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.system.features.hybridGPU`
`bool`, default `false`. Enables `services.supergfxd.enable` for laptops with switchable GPUs.
`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`
@@ -135,10 +153,18 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
`bool`, default `false`. Intel IPU7 camera support (kernel modules + firmware).
### `impermanence.enable`
### `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`)
@@ -179,10 +205,6 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
`bool`, default `true`. Whether the top bar is deployed at all.
### `nomarchy.toggles.skipVsCodeTheme`
`bool`, default `false`. Skip theme overrides in VSCode — useful if you manage VSCode themes yourself.
### `nomarchy.nightlightTemperature`
`int`, default `4000`. Nightlight color temperature (Kelvin).
@@ -205,11 +227,11 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.iconsTheme`
`str`, default `"Yaru-blue"`. GTK/Qt icon theme name.
`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 `false`. Whether the active theme is a light theme. Affects nightlight defaults and a few app theme decisions.
`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`
@@ -221,23 +243,7 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.configOverrides`
`nullOr path`, default `null`. Path to a directory containing config overrides. See "Overrides" below.
### `nomarchy.behavior.hyprland.bindings`
`bool`, default `true`. Deploy the default Hyprland keybindings. Set to `false` if you want to write `bindings.conf` from scratch.
### `nomarchy.behavior.hyprland.input`
`bool`, default `true`. Deploy default input settings (kb_layout, mouse accel, etc).
### `nomarchy.behavior.hyprland.windowRules`
`bool`, default `true`. Deploy default window rules.
### `nomarchy.behavior.hyprland.autostart`
`bool`, default `true`. Deploy the default `autostart.conf` (hypridle, mako, swayosd, nm-applet, etc).
`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`
@@ -253,19 +259,19 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.themeLoader.enable`
`bool`, default `true`. Auto-load theme-specific app configs (btop, waybar, mako, kitty, alacritty) from the active theme. Disable if you want to provide your own.
`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,waybar,mako,kitty,alacritty}`
### `nomarchy.themeLoader.apps.btop`
`bool`, default `true` each. Per-app toggles for the theme loader — pick which apps follow the active theme.
`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`. Enable file-based overrides loaded from `~/.config/nomarchy/overrides/`. With this on, Nomarchy looks for matching files in that directory and substitutes them for the bundled defaults.
`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 `{}`. Override paths discovered at build time. Populated by the override system — you don't normally set this directly.
`attrsOf path`, default `{}`. **Reserved — currently unused.** Will be populated by the future override loader.
---
@@ -297,15 +303,17 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
}
```
### Disable Nomarchy's default Hyprland keybindings to ship your own
### 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
{
nomarchy.behavior.hyprland.bindings = false;
xdg.configFile."hypr/bindings.conf".source = ./my-bindings.conf;
}
```
Then put your own `bindings.conf` at `~/.config/nomarchy/overrides/hypr/bindings.conf` (with `nomarchy.overrides.enable = true;`, which is the default).
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.
---
@@ -315,7 +323,6 @@ Then put your own `bindings.conf` at `~/.config/nomarchy/overrides/hypr/bindings
- `core/system/hardware.nix``nomarchy.hardware.*`
- `core/system/impermanence.nix``impermanence.enable`
- `core/home/options.nix` — most home-side `nomarchy.*` options
- `core/home/behavior.nix``nomarchy.behavior.*`
- `core/home/overrides.nix``nomarchy.overrides.*`
- `core/home/overrides.nix``nomarchy.overrides.*` (reserved; currently no-op)
- `themes/engine/loader.nix``nomarchy.themeLoader.*`
- `features/apps/vscode.nix``nomarchy.vscode.*`

View File

@@ -19,13 +19,16 @@ Guardrails (apply when adding anything):
### Now (ready to pick up)
- (Empty for now)
- **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)
@@ -37,6 +40,14 @@ Guardrails (apply when adding anything):
- **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.
- **Package missing VSCode theme extensions via `extensionFromVscodeMarketplace`.** 15 of the 21 palettes that ship a `themes/palettes/<theme>/apps/vscode.json` declare a theme extension that isn't in `pkgs.vscode-extensions` — including `sainnhe.everforest` which is the default `summer-night` palette's theme. With the `577b3ae` fix in place, the 6 nixpkgs-packaged extensions install by default (catppuccin, catppuccin-latte, nord, tokyo-night, rose-pine, gruvbox), but the other 15 (`sainnhe.everforest`, `qufiwefefwoyn.kanagawa`, `monokai.theme-monokai-pro-vscode`, `oldjobobo.{lumon,miasma,retro-82}-theme`, `Bjarne.{ethereal,hackerman,vantablack,white}-nomarchy`, `shadesOfBuntu.flexoki-light`, `jovejonovski.ocean-green`, `TahaYVR.matteblack`) still leave `workbench.colorTheme` referencing an unloaded theme, so VSCode silently falls back. Fix: extend `features/apps/vscode.nix` to look up the active palette's extension via `pkgs.vscode-utils.extensionFromVscodeMarketplace { publisher; name; version; sha256; }` — each entry pinned by hash. Could be table-driven in `lib/` so a new palette only needs to add a row.
- **Reconcile nightlight: systemd-managed hyprsunset vs `pkill`/`hyprctl exec` toggle.** `features/desktop/nightlight.nix` always sets `services.hyprsunset.enable = lib.mkDefault true` and bakes the temperature (`toggles.nightlight ? nightlightTemperature : 6500`) into `extraArgs` at Nix-eval time. The toggle script `themes/engine/scripts/nomarchy-toggle-nightlight` bypasses systemd entirely: `pkill hyprsunset` on disable, `hyprctl dispatch exec hyprsunset --temperature 4000` on enable — racing the systemd-managed instance and ignoring the user's chosen `nightlightTemperature`. The comment "Always enabled, we control via IPC and state" doesn't match the implementation (there's no IPC; the temperature is rebuild-time, not runtime). Pick one: (a) toggle stays as `hyprctl dispatch hyprsunset temperature ${T}` (proper IPC, no daemon restart), with Nix dropping `extraArgs` to just `--temperature 6500` on boot; or (b) gate `services.hyprsunset.enable` on the toggle and run `systemctl --user restart hyprsunset` from the script. Both also need the script to read `nightlightTemperature` instead of hardcoding 4000.
## 3. Pillar: Script & menu audit
@@ -73,31 +84,31 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
## 4. Pillar: Installer
- Software-profile multi-select (Now).
- "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).
- Form-factor → laptop preset (Now, depends on Pillar 5).
- `disko-golden.nix` variants for software-RAID and BTRFS-pool-as-root (Shipped).
- Pre-flight resume polish (Next).
- "What's installed?" summary screen on boot of a freshly-installed system, sourced from `state.json` + `nomarchy-system-scripts` introspection.
- Optional non-LUKS branch in the installer for users who explicitly opt out of FDE.
- Pre-flight resume polish (Shipped).
- Software-profile multi-select (Shipped).
- Form-factor → laptop preset (Shipped).
## 5. Pillar: Power, hardware, presets
- Laptop preset (Next): TLP, upower, brightness, lid, hypridle tuning.
- Desktop preset (Next): performance governor, no laptop UI (already filtered), ZFS hooks.
- Accessibility preset (Next).
- Gaming preset (Next).
- Vendor matchers in `installer/hardware-db.sh`: Steam Deck, Surface, ROG Ally, Snapdragon X laptops.
- 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).
- Auto-detect dGPU presence and offer `programs.envycontrol`-style switching for the hybrid case (already gated behind `nomarchy.system.features.hybridGPU`, but the wiring is minimal).
- 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 (Next).
- `nomarchy-welcome` first-run wizard (Shipped).
- `docs/KEYBINDINGS.md` auto-generator (Shipped).
- `docs/TROUBLESHOOTING.md` (Next).
- `docs/index.md` (or just enrich `README.md`) so `OPTIONS.md`, `STRUCTURE.md`, `MIGRATION.md`, `ROADMAP.md`, `SCRIPTS.md`, and `creating-themes.md` are all one click from the front page.
- `nomarchy-manual` — orphaned reference today; either implement as a curated `xdg-open` to the docs index, or delete.
- `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
@@ -109,6 +120,33 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
- `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`.
@@ -121,6 +159,16 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
(Move items here when they land — keep them brief, link the commit/PR.)
- _2026-05-21_ — **Pillar 8 / Component 6 (Apps): closeout.** Code-review-shaped sweep over `features/apps/{alacritty,btop,chromium,elephant,ghostty,kitty,lazygit,opencode,swayosd,tmux,vscode,walker}`. Three real theming bugs fixed inline: **(1)** `features/apps/kitty/config/kitty.conf:1` and `features/apps/ghostty/config/config:2` referenced palette-specific include files (`~/.config/nomarchy/current/theme/{kitty,ghostty}.conf`) that didn't exist for any of the 22 palettes — kitty include failed silently, ghostty's was optional (`?`-prefix), and both terminals rendered with their built-in defaults regardless of the active Nomarchy palette. Stylix's `kitty.enable = true` was a no-op because the module uses `xdg.configFile` instead of `programs.kitty`; ghostty has no Stylix target. Added theme-engine generators in `themes/engine/files.nix` mirroring the existing `waybar.css` pattern, mapping `palette.base*` to kitty/ghostty color directives. **(2)** `features/apps/btop/config/btop.conf:5` set `color_theme = "current"` but `themes/engine/loader.nix:72` deploys the active palette's btop theme to `~/.config/btop/themes/nomarchy.theme` — name mismatch, btop fell back to its built-in Default theme on every palette. Renamed to `"nomarchy"`. **(3)** `programs.vscode.profiles.default.userSettings.workbench.colorTheme` was set unconditionally from `themes/palettes/<theme>/apps/vscode.json`, but the matching theme extensions were bundled with `devExtensions` (default `false`) — so VSCode silently fell back to its built-in theme out of the box on every palette. Split `themeExtensions` (always-on, covers the 6 palettes whose theme extension is in nixpkgs) from `devExtensions` (opt-in). The remaining 15 palettes — including the default `summer-night` (`sainnhe.everforest`) — still break because their theme extensions aren't packaged in nixpkgs; logged as a new Later row. Chromium static `Default/Preferences` symlink already had an open Later row; verified the file's contents are duplicate of the managed-policy intent in `core/system/browser.nix`, so the existing entry's hypothesis is correct — left for the user to greenlight deletion. alacritty (Stylix-themed via `programs.alacritty.settings`), elephant (no UI), swayosd (base16 inline), walker (covered in Component 5), lazygit + tmux (terminal ANSI inheritance, transitively fixed by the kitty/ghostty changes), and opencode (minimal opt-in config) are healthy. Runtime verification (launch each app on each palette and eyeball the theming) remains the user's responsibility.
- _2026-05-21_ — **Pillar 8 / Component 5 (Desktop stack): closeout.** Code-review-shaped sweep over Hyprland, waybar, walker, hypridle, hyprsunset, mako, KEYBINDINGS.md (the runtime-rendering subset — waybar across panel positions × form factors × all 22 palettes, walker launcher modes, hypridle timeout feel — stays on the user). Five real bugs fixed inline: **(1)** 9 of 17 `~/.config/nomarchy/default/hypr/apps/*.conf` window-rule files were deployed but never sourced, including `system.conf` (the `tag +floating-window` rules every TUI helper class relies on + `class:org.nomarchy.screensaver` fullscreen rule that hypridle's 150s on-timeout depends on) and `pip.conf` (the PiP pin/size rules). `apps.conf` now sources all 17. **(2)** Two `$NNOMARCHY_TOGGLE_*` typos (double-N) in `nomarchy-menu:330` and `nomarchy-launch-screensaver:16` made `toggles.suspend` and `toggles.screensaver` vacuous — Suspend always showed in the system menu and the screensaver always launched at idle regardless of the documented option. **(3)** 4 broken per-palette waybar `style.css` overrides (`catppuccin`, `lumon`, `nord`, `retro-82`) fully replaced the default style with 214 lines of only `@define-color` declarations — picking those palettes produced a waybar with zero structural styling. Default style already imports per-palette colors via `themes/engine/files.nix`-generated `theme/waybar.css`, so deletion restores correct rendering; `summer-day`/`summer-night` kept as legitimate 100+-line redesigns. **(4)** `core/home/config/nomarchy/default/hypr/{bindings,plain-bindings}.conf` were explicitly-labeled deprecated files sourced by nothing (plain-bindings.conf referenced undefined `$terminal`/`$browser`/etc. Hyprland vars) — deleted; `docs/SCRIPTS.md` regenerated to drop stale callers and incidentally corrected 4 Origin columns whose scripts moved from `core/system/scripts/` to `features/scripts/utils/`. **(5)** Mako post-fix (commit `2a301a0`) verified: deployment + the 4 referenced scripts (`nomarchy-notification-dismiss`, `nomarchy-launch-wifi`, `nomarchy-launch-floating-terminal-with-presentation`, `nomarchy-menu-keybindings`) all resolve. Two structural inconsistencies logged to Later: keymap routing (already in 72f7e7b) and the new hyprsunset toggle-vs-systemd reconcile. `KEYBINDINGS.md` regenerated with zero diff — generator already covers both binding source locations. Runtime verification (boot live ISO, eyeball waybar/walker/screensaver flows across panel positions and palettes) remains the user's responsibility before declaring Component 5 fully closed.
- _2026-05-18_ — Hardware DB correctness pass + ROG Ally support + CI lint. Audited every `nomarchy-hardware-db` entry against `inputs.nixos-hardware.nixosModules` and found **21 of 43 entries (49%) referenced modules that don't exist**`microsoft-surface-pro-8`, `lenovo-thinkpad-x1-carbon-gen11`, `framework-13-11th-gen-intel`, etc. were all eval-time failures waiting for a real user. Rewrote the DB to use only valid module names: Framework gens dropped the "13-" prefix in nixos-hardware (`framework-11th-gen-intel`, not `framework-13-11th-gen-intel`); ThinkPad X1 modules are `x1-Nth-gen`, not `x1-carbon-genN`; Surface Pro 6/7/8/10 all share `microsoft-surface-pro-intel`; Surface Book / Intel Surface Laptop have no module (rows dropped, generic detection still emits sensible `common-pc-laptop` + cpu/gpu). Added matchers for **ROG Ally** (RC71L / RC72LA / "ROG Ally" via `asus-ally-rc71l`). Documented Steam Deck and Snapdragon X as nixos-hardware-unsupported in a footer comment (Steam Deck → Jovian-NixOS; Snapdragon X → installer is x86_64 only). Added a CI step (`.forgejo/workflows/check.yml`) that fails on any DB entry whose module name isn't in `nixos-hardware.nixosModules` — closes this regression class.
- _2026-05-18_ — `nomarchy-manual` re-targeted at local docs. The script's `xdg-open` previously pointed at `https://learn.omacom.io/2/the-nomarchy-manual` — an upstream Omarchy URL that opened an unrelated page when users hit the menu's Help entry. Now opens `~/.local/share/nomarchy/README.md` (the local docs index per `SKILL.md`'s "Out of Scope" note), with a `notify-send` fallback if the source tree isn't synced.
- _2026-05-18_ — Docs hygiene: STRUCTURE.md "Root Directory" + Pillar 6 reality-check. `docs/STRUCTURE.md` listed three top-level files that don't exist (`GEMINI.md`, root-level `STRUCTURE.md`, `TODO.md`) — replaced with an accurate root listing plus a `docs/` sub-tree that names every doc. Pillar 6 in this file had `nomarchy-welcome`, `docs/TROUBLESHOOTING.md`, and the "docs index" bullet still marked Next despite all three shipping on 2026-04-26 — moved to `(Shipped)`. `nomarchy-manual` bullet's "orphaned reference today" claim was stale (the script is called from `nomarchy-menu` and `nomarchy-theme-install`); rewritten to reflect the real remaining issue — its hardcoded `xdg-open https://learn.omacom.io/2/the-nomarchy-manual` is an Omarchy URL.
- _2026-05-18_ — Installer state.json is now schema-driven. Replaced the heredoc in `installer/install.sh` that hardcoded the JSON literal (theme/dns/wifi/features/etc.) with a `nix eval` of `lib/state-schema.nix`'s `system` block, overlaid with the installer-chosen timezone. Closes the last source-of-truth split after the centralization batch — adding a new default in the schema now reaches the installer with no further plumbing. Output is identical modulo alphabetical key ordering (Nix's `builtins.toJSON` sorts keys; toggle scripts read/write via `jq` so it's invisible to them). Dry-run path unchanged (still bind-mounts a fake `/mnt` so the generator's absolute paths resolve correctly). `bash -n` + `shellcheck --severity=error` clean.
- _2026-05-18_ — Complete the hybrid-GPU wiring + fix unoverridable state-derived options. Two related fixes shipped together. **(1)** `nomarchy.system.features.hybridGPU = true` now wires the full NVIDIA driver stack (`services.xserver.videoDrivers = ["nvidia"]`, `hardware.graphics.{enable,enable32Bit}`, `hardware.nvidia.{modesetting,powerManagement}.enable`, `package = nvidiaPackages.stable`, `boot.kernelParams += "nvidia-drm.modeset=1"`) — was previously enabling only `supergfxd` mode-switching while leaving the system with no NVIDIA driver loaded, so mode switches silently no-op'd. All knobs use `lib.mkDefault` so a downstream `system.nix` can pin a beta driver, flip to the open kernel module, etc. Bus-ID prime config (per-machine) stays user-supplied — `docs/OPTIONS.md` has the full recipe. **(2)** Both `core/system/state.nix` and `core/home/state.nix` now use `lib.mkDefault` on every state.json-derived assignment, fixing a class of "I set X in my system.nix but it doesn't take effect" bugs (the state-derived value was at default priority and conflicted with the user's same-priority override). Side-effect cleanup: `core/system/state.nix` now also reads from `lib/state-schema.nix` like `core/home/state.nix` does, completing the schema-centralization started two batches ago. Verified `nix flake check` + an override test that flips hybridGPU via an overlay and confirms the entire driver stack engages.
- _2026-05-18_ — Pillar 4: pre-flight resume polish. Fixed four resume-flow gaps in `installer/install.sh`: (1) `--resume` with a missing state file now errors loudly with a tmpfs explanation instead of silently falling through to a fresh prompt cycle (the most common operator confusion was "rebooted, forgot tmpfs eats /tmp/, watched the installer start over without realising"); (2) on resume, the saved target drive is validated as a block device before any disk-phase step runs — catches the live-ISO USB-unplugged / non-deterministic /dev/sdX class of mid-install failures; (3) `save_state` now stamps an ISO-8601 timestamp and `load_state` shows a `(saved Xm ago)` banner plus a `Target: /dev/X → user @ host` summary line, so the user can `Ctrl-C` if they're resuming onto the wrong host before any destructive prompt fires; (4) `--help` now documents the tmpfs limitation. `shellcheck --severity=error` passes.
- _2026-05-18_ — Declarative-state defaults centralization. Made `lib/state-schema.nix` the single source of truth for every state-default that previously lived in three places (the schema itself, `core/system/options.nix` / `core/home/options.nix` `default = …` clauses, and `core/home/state.nix` `or …` fallbacks). Replaced ~25 hardcoded literals with `schema.<scope>.<key>` reads. Side-effect: fixed a lingering bug where `core/home/options.nix:theme` still defaulted to `"summer-night"` after the system-side was moved to `"nord"` — half the codebase's home option resolved to the wrong theme when state.json was missing/blank. `nix flake check --no-build` confirms zero semantic change for every other field. Doesn't touch the installer-written `state.json` (separate batch — needs schema → JSON generation).
- _2026-05-18_ — Pillar 7 first step: Forgejo Actions CI (eval + lint). New `.forgejo/workflows/check.yml` runs on every push to `main` and every PR: (1) `nix flake check --no-build` to catch eval regressions, (2) `bash -n` + `shellcheck --severity=error` over every `nomarchy-*` bash script (whole-tree, not just changed files — gates branches that bypass the pre-commit hook), (3) `docs/SCRIPTS.md` drift check (fails loudly if a script change didn't regenerate the audit doc). All three checks pass locally on the current tree. Activation requires enabling Actions on the Forgejo repo and registering a `forgejo-runner`; the workflow itself is dormant until then. ISO build job is intentionally deferred — needs a binary cache (Cachix/Attic) to be tractable.
- _2026-05-18_ — **Pillar 3 Phase B: complete.** Final batch (restart/sudo/theme/misc clusters) cleared the last 13 `unused?` rows. Deleted five truly dead scripts: `nomarchy-restart-{hyprctl,mako}` (theme switching calls `hyprctl reload`/`makoctl reload` directly now), `nomarchy-restart-tmux` (one-liner of marginal value), `nomarchy-battery-present` (battery monitor checks `/sys/class/power_supply/BAT*` inline), `nomarchy-sudo-keepalive` (intended-to-be-sourced building block with no users). Surfaced eight useful tools in `SKILL.md` so the audit catches them as `kept` and AI assistants can discover them: `nomarchy-restart-trackpad` (intel_quicki2c reload), `nomarchy-sudo-{passwordless-toggle,reset}`, `nomarchy-theme-{bg-install,refresh,remove}`, `nomarchy-refresh-fastfetch`, `nomarchy-windows-vm` (new Virtualization section). Final state: 159 scripts, all `kept`, `unused?` = 0, missing references = 0.
- _2026-05-18_ — Pillar 3 Phase B: webapp/tui/voxtype install-remove pair triage. Deleted two dead webapp URI handlers (`nomarchy-webapp-handler-hey`, `nomarchy-webapp-handler-zoom`) — no `.desktop` MimeType registration anywhere routed `mailto:`/`zoom:` URIs to them, so the handlers could never fire. Surfaced six useful CLI tools in `SKILL.md` "Common Tasks" so they're discoverable by AI assistants and tagged `kept` by the audit: `nomarchy-webapp-{remove,remove-all}`, `nomarchy-tui-{remove,remove-all}`, `nomarchy-voxtype-{install,remove}`. Script count 166 → 164; `unused?` 21 → 13.
- _2026-05-18_ — Pillar 3 Phase B: dead-code sweep (NixOS-irrelevant Omarchy ports). Deleted five scripts that duplicated NixOS-native facilities or referenced infrastructure Nomarchy doesn't ship: `nomarchy-rollback` (boot-menu generations + `nixos-rebuild rollback` already cover this), `nomarchy-snapshot` (used `snapper`; impermanence and BTRFS subvolumes are the Nomarchy answer), `nomarchy-migrate-state` (one-shot pre-unification migration, no current callers), `nomarchy-config-direct-boot` (added an EFI entry for a UKI we never build), and `nomarchy-npx-install` (Arch idiom — `nix-shell -p nodejs` is the NixOS path). Kept `nomarchy-build-iso` and `nomarchy-build-live-iso` and surfaced them in README §2 so the audit tags them `kept`. Script count 171 → 166.

View File

@@ -45,14 +45,14 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `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-cmd-terminal-cwd` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-debug` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-docs-keybindings` | `features/scripts/utils` | bin/utils/nomarchy-docs-keybindings | `kept` | |
| `nomarchy-docs-scripts` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts | `kept` | |
| `nomarchy-drive-info` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select | `kept` | |
| `nomarchy-drive-select` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-info,features/scripts/utils/nomarchy-drive-set-password | `kept` | |
| `nomarchy-drive-set-password` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-env-update` | `core/system/scripts` | core/home/bash.nix,core/system/scripts.nix, +7 more | `kept` | |
| `nomarchy-env-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts.nix, +7 more | `kept` | |
| `nomarchy-font` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy-skill/SKILL.md, +6 more | `kept` | |
| `nomarchy-font-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-font-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-font, +2 more | `kept` | |
@@ -77,8 +77,8 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `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-browser` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-launch-floating-terminal-with-presentation` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-or-focus` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/hyprland/config/bindings.conf, +6 more | `kept` | |
| `nomarchy-launch-or-focus-tui` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
@@ -86,21 +86,21 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `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-webapp` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-launch-or-focus-webapp, +4 more | `kept` | |
| `nomarchy-launch-wifi` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +4 more | `kept` | |
| `nomarchy-lock-screen` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/extensions/menu.sh, +3 more | `kept` | |
| `nomarchy-manual` | `features/scripts/utils` | core/branding/about.txt,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-menu` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +10 more | `kept` | |
| `nomarchy-menu` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +9 more | `kept` | |
| `nomarchy-menu-keybindings` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +2 more | `kept` | |
| `nomarchy-notification-dismiss` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini | `kept` | |
| `nomarchy-on-boot` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf | `kept` | |
| `nomarchy-pkg-add` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-pkg-install, +2 more | `kept` | |
| `nomarchy-pkg-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-pkg-install, +2 more | `kept` | |
| `nomarchy-pkg-aur-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-pkg-drop` | `features/scripts/utils` | features/scripts/utils/nomarchy-voxtype-remove | `kept` | |
| `nomarchy-pkg-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-pkg-remove` | `core/system/scripts` | features/scripts/utils/nomarchy-pkg-drop | `kept` | |
| `nomarchy-pkg-remove` | `features/scripts/utils` | features/scripts/utils/nomarchy-pkg-drop | `kept` | |
| `nomarchy-powerprofiles-list` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-preflight-migration` | `core/system/scripts` | core/system/scripts/nomarchy-env-update | `kept` | |
| `nomarchy-preflight-migration` | `features/scripts/utils` | features/scripts/utils/nomarchy-env-update | `kept` | |
| `nomarchy-refresh-config` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-refresh-fastfetch | `kept` | |
| `nomarchy-refresh-fastfetch` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-refresh-hyprland` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
@@ -169,7 +169,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-tui-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-tui-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-tz-select` | `core/system/scripts` | features/desktop/waybar/config/config.jsonc,features/scripts/utils/nomarchy-menu, +2 more | `kept` | |
| `nomarchy-update` | `core/system/scripts` | core/home/config/nomarchy/default/mako/core.ini,core/home/config/nomarchy-skill/SKILL.md, +3 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` | |
@@ -184,7 +184,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `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-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf,lib/state-schema.nix | `kept` | |
| `nomarchy-wifi-powersave` | `core/system/scripts` | features/scripts/utils/nomarchy-sys-update,installer/install.sh | `kept` | |
| `nomarchy-windows-vm` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |

View File

@@ -33,10 +33,19 @@ While the system is defined declaratively, Nomarchy uses a small, local state fi
- `nixosModules.home`: Exports the application and desktop logic (`./features`).
- `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).

View File

@@ -1,25 +0,0 @@
general.import = [ "~/.config/nomarchy/current/theme/alacritty.toml" ]
[env]
TERM = "xterm-256color"
[terminal]
osc52 = "CopyPaste"
[font]
normal = { family = "JetBrainsMono Nerd Font", style = "Regular" }
bold = { family = "JetBrainsMono Nerd Font", style = "Bold" }
italic = { family = "JetBrainsMono Nerd Font", style = "Italic" }
size = 9
[window]
padding.x = 14
padding.y = 14
decorations = "None"
[keyboard]
bindings = [
{ key = "Insert", mods = "Shift", action = "Paste" },
{ key = "Insert", mods = "Control", action = "Copy" },
{ key = "Return", mods = "Shift", chars = "\u001B\r" }
]

View File

@@ -2,7 +2,7 @@
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
color_theme = "current"
color_theme = "nomarchy"
#* If the theme set background should be shown, set to False if you want terminal background transparency.
theme_background = True

View File

@@ -7,7 +7,26 @@ let
else
{ name = "Default Dark Modern"; };
# Development extensions that match the system theme
# Theme extensions matching palette vscode.json `extension` fields. Always
# installed because workbench.colorTheme is set unconditionally from the
# active palette — without the matching extension VSCode silently falls
# back to its default theme. Only the extensions available in
# pkgs.vscode-extensions are listed. Palettes whose theme extension is on
# the VSCode marketplace but not packaged in nixpkgs (sainnhe.everforest —
# affects the DEFAULT summer-night palette — plus qufiwefefwoyn.kanagawa,
# monokai.theme-monokai-pro-vscode, oldjobobo.*, Bjarne.*,
# shadesOfBuntu.flexoki-light, jovejonovski.ocean-green, TahaYVR.matteblack)
# still break and need pkgs.vscode-utils.extensionFromVscodeMarketplace —
# tracked in ROADMAP Later.
themeExtensions = with pkgs.vscode-extensions; [
catppuccin.catppuccin-vsc # catppuccin, catppuccin-latte
enkia.tokyo-night # tokyo-night
arcticicestudio.nord-visual-studio-code # nord
mvllow.rose-pine # rose-pine
jdinhlife.gruvbox # gruvbox
];
# Development extensions — opt-in via nomarchy.vscode.devExtensions.
devExtensions = with pkgs.vscode-extensions; [
# Language support
ms-python.python
@@ -22,13 +41,6 @@ let
esbenp.prettier-vscode
dbaeumer.vscode-eslint
bradlc.vscode-tailwindcss
# Theme extensions (provide color themes matching nomarchy palettes)
catppuccin.catppuccin-vsc
enkia.tokyo-night
arcticicestudio.nord-visual-studio-code
sainnhe.everforest
mvllow.rose-pine
];
in
{
@@ -51,7 +63,10 @@ in
"editor.fontFamily" = "'${config.nomarchy.fonts.monospace}', 'Droid Sans Mono', monospace";
"terminal.integrated.fontFamily" = config.nomarchy.fonts.monospace;
};
extensions = lib.mkIf config.nomarchy.vscode.devExtensions (lib.mkDefault devExtensions);
extensions = lib.mkDefault (
themeExtensions
++ lib.optionals config.nomarchy.vscode.devExtensions devExtensions
);
};
};
}

View File

@@ -2,14 +2,6 @@
let
nomarchyLib = import ../lib { inherit lib; };
userPackagesFile = "${config.home.homeDirectory}/.config/home-manager/user-packages.json";
userPackages = if builtins.pathExists userPackagesFile then
let
pkgNames = builtins.fromJSON (builtins.readFile userPackagesFile);
# Filter to only packages that exist in pkgs to prevent build failures
validPkgs = builtins.filter (name: builtins.hasAttr name pkgs) pkgNames;
in builtins.map (name: pkgs.${name}) validPkgs
else [];
in
{
imports = [

View File

@@ -1,71 +0,0 @@
# paths to stylesheets on the filesystem which should be applied to the application
#
# relative paths are resolved relative to the location of the config file
stylesheets: ["../nomarchy/current/theme/hyprland-preview-share-picker.css"]
# default page selected when the picker is opened
default_page: outputs
window:
# height of the application window
height: 500
# width of the application window
width: 1000
image:
# size to which the images should be internally resized to reduce the memory footprint
resize_size: 500
# target size of the longer side of the image widget
widget_size: 150
classes:
# css classname of the window
window: window
# css classname of the card containing an image and a label
image_card: card
# css classname of the card containing an image and a label when the image is still being loaded
image_card_loading: card-loading
# css classname of the image inside the card
image: image
# css classname of the label inside the card
image_label: image-label
# css classname of the notebook containing all pages
notebook: notebook
# css classname of a label of the notebook
tab_label: tab-label
# css classname of a notebook page (e.g. windows container)
notebook_page: page
# css classname of the region selection button
region_button: region-button
# css classname of the button containing the session restore checkbox and label
restore_button: restore-button
windows:
# minimum amount of image cards per row on the windows page
min_per_row: 3
# maximum amount of image cards per row on the windows page
max_per_row: 999
# number of clicks needed to select a window
clicks: 1
# spacing in pixels between the window cards
spacing: 12
outputs:
# number of clicks needed to select an output
clicks: 1
# spacing in pixels between the outputs in the layout
# note: the spacing is applied from both sides (the gap is `spacing * 2`)
spacing: 6
# show the label with the output name
show_label: false
# size the output cards respectively to their scaling
respect_output_scaling: true
region:
# command to run for region selection
# the output needs to be in the <output>@<x>,<y>,<w>,<h> (e.g. DP-3@2789,436,756,576) format
command: slurp -f '%o@%x,%y,%w,%h'
# hide the token restore checkbox and use the default value instead
hide_token_restore: true
# enable debug logs by default
debug: false

View File

@@ -1,2 +0,0 @@
# Extra autostart processes
# exec-once = uwsm-app -- my-service

View File

@@ -1,43 +0,0 @@
source = ~/.config/nomarchy/current/theme/hyprlock.conf
general {
ignore_empty_input = true
}
background {
monitor =
color = $color
path = ~/.config/nomarchy/current/background
blur_passes = 3
}
animations {
enabled = false
}
input-field {
monitor =
size = 650, 100
position = 0, 0
halign = center
valign = center
inner_color = $inner_color
outer_color = $outer_color
outline_thickness = 4
font_family = JetBrainsMono Nerd Font
font_color = $font_color
placeholder_text = Enter Password
check_color = $check_color
fail_text = <i>$FAIL ($ATTEMPTS)</i>
rounding = 0
shadow_passes = 0
fade_on_empty = false
}
auth {
fingerprint:enabled = false
}

View File

@@ -1,14 +0,0 @@
# Makes hyprsunset do nothing to the screen by default
# Without this, the default applies some tint to the monitor
profile {
time = 07:00
identity = true
}
# To enable auto switch to nightlight, set in your .config/hypr/autostart:
# exec-once = uwsm app -- hyprsunset
# and use the following:
# profile {
# time = 20:00
# temperature = 4000
# }

View File

@@ -1,34 +0,0 @@
# Change the default Nomarchy look'n'feel
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
# No gaps between windows or borders
# gaps_in = 0
# gaps_out = 0
# border_size = 0
# Change to niri-like side-scrolling layout
# layout = scrolling
}
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
# Use round window corners
# rounding = 8
# Dim unfocused windows (0.0 = no dim, 1.0 = fully dimmed)
# dim_inactive = true
# dim_strength = 0.15
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
# Disable all animations
# enabled = no
}
# https://wiki.hypr.land/Configuring/Variables/#layout
layout {
# Avoid overly wide single-window layouts on wide screens
# single_window_aspect_ratio = 1 1
}

View File

@@ -1,4 +0,0 @@
screencopy {
allow_token_by_default = true
custom_picker_binary = hyprland-preview-share-picker
}

View File

@@ -48,13 +48,14 @@ in
'';
};
# Deploy Hyprland configuration files
# Deploy Hyprland configuration files. Only the files that nomarchy.conf
# actually sources are deployed here — looknfeel.conf and autostart.conf
# live under ~/.config/nomarchy/default/hypr/ and are deployed by the
# core/home bulk-nomarchy dir, so duplicating them here was dead surface.
xdg.configFile."hypr/nomarchy.conf".source = ./config/nomarchy.conf;
xdg.configFile."hypr/monitors.conf".source = lib.mkDefault ./config/monitors.conf;
xdg.configFile."hypr/input.conf".source = lib.mkDefault ./config/input.conf;
xdg.configFile."hypr/bindings.conf".source = lib.mkDefault ./config/bindings.conf;
xdg.configFile."hypr/looknfeel.conf".source = lib.mkDefault ./config/looknfeel.conf;
xdg.configFile."hypr/autostart.conf".source = lib.mkDefault ./config/autostart.conf;
# Run swaybg as a proper systemd user service rather than a Hyprland exec-once.
# exec-once fails silently (black screen with no visible error) when timing

View File

@@ -1,2 +0,0 @@
@define-color foreground #cdd6f4;
@define-color background #181824;

View File

@@ -1,2 +0,0 @@
@define-color foreground #d6e2ee;
@define-color background #213442;

View File

@@ -1,14 +0,0 @@
@define-color background #2e3440;
@define-color foreground #d8dee9;
@define-color accent #88c0d0;
/* Base style for Nord */
* {
font-family: JetBrainsMono Nerd Font, FontAwesome;
font-size: 13px;
}
window#waybar {
background-color: @background;
color: @foreground;
}

View File

@@ -1,3 +0,0 @@
@define-color bg #00172e;
@define-color foreground #f6dcac;
@define-color background alpha(@bg, 0.8);

View File

@@ -71,7 +71,6 @@ let
NOMARCHY_TOGGLE_IDLE = if config.nomarchy.toggles.idle then "true" else "false";
NOMARCHY_TOGGLE_NIGHTLIGHT = if config.nomarchy.toggles.nightlight then "true" else "false";
NOMARCHY_TOGGLE_WAYBAR = if config.nomarchy.toggles.waybar then "true" else "false";
NOMARCHY_TOGGLE_SKIP_VSCODE_THEME = if config.nomarchy.toggles.skipVsCodeTheme then "true" else "false";
NOMARCHY_MONOSPACE_FONT = config.nomarchy.fonts.monospace;
};

View File

@@ -13,7 +13,7 @@ pgrep -f org.nomarchy.screensaver && exit 0
# Allow screensaver to be turned off but also force started
# Skip if screensaver is disabled in configuration
if [[ $NNOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then
if [[ $NOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then
exit 0
fi

View File

@@ -1,10 +1,18 @@
#!/usr/bin/env bash
set -e
# Nomarchy Manual Script
# Opens the Nomarchy manual in the default web browser.
# Open the Nomarchy docs index in the default handler.
# On an installed system the source tree lives at ~/.local/share/nomarchy/
# (see SKILL.md's "Out of Scope" section), so the README — which links every
# doc in docs/ — is the canonical "open the manual" target.
URL="https://learn.omacom.io/2/the-nomarchy-manual"
README="$HOME/.local/share/nomarchy/README.md"
echo "Opening Nomarchy manual: $URL"
xdg-open "$URL"
if [[ -f "$README" ]]; then
echo "Opening Nomarchy manual: $README"
xdg-open "$README"
else
notify-send "Nomarchy Manual" \
"Source tree not found at $README. Try \`nomarchy-update\` to sync it."
exit 1
fi

View File

@@ -327,7 +327,7 @@ show_about() {
show_system_menu() {
local options="󱄄 Screensaver\n Lock"
[[ $NNOMARCHY_TOGGLE_SUSPEND != "false" ]] && options="$options\n󰒲 Suspend"
[[ $NOMARCHY_TOGGLE_SUSPEND != "false" ]] && options="$options\n󰒲 Suspend"
nomarchy-hibernation-available && options="$options\n󰤁 Hibernate"
options="$options\n󰍃 Logout\n󰜉 Restart\n󰐥 Shutdown"

View File

@@ -28,16 +28,25 @@ echo ""
# 1. Select initial theme
echo "Step 1: Choose your starting theme"
nomarchy-theme-set "$(nomarchy-theme-list | gum filter --placeholder 'Select a theme...')" --no-update
SELECTED_THEME="$(nomarchy-theme-list | gum filter --placeholder 'Select a theme...')"
if [[ -n "$SELECTED_THEME" ]]; then
THEME_ID=$(echo "$SELECTED_THEME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
nomarchy-theme-set "$THEME_ID" --no-update
fi
# 2. Select initial font
echo "Step 2: Choose your preferred font"
nomarchy-font-set "$(nomarchy-font-list | gum filter --placeholder 'Select a font...')" --no-update
SELECTED_FONT="$(nomarchy-font-list | gum filter --placeholder 'Select a font...')"
if [[ -n "$SELECTED_FONT" ]]; then
nomarchy-font-set "$SELECTED_FONT" --no-update
fi
# 3. Select panel position
echo "Step 3: Choose your preferred panel position"
POSITION=$(gum choose "top" "bottom")
nomarchy-state-write panelPosition "$POSITION"
if [[ -n "$POSITION" ]]; then
nomarchy-state-write panelPosition "$POSITION"
fi
# Skip system-modifying steps in the Live ISO environment
if [[ "$USER" == "nixos" ]]; then
@@ -50,39 +59,9 @@ if [[ "$USER" == "nixos" ]]; then
exit 0
fi
# 4. Starter home.nix
# 4. Setup Local Repo (Crucial for nomarchy-env-update to work)
echo ""
echo "Step 4: Starter home.nix"
HOME_NIX="$HOME/.config/home-manager/home.nix"
if [ ! -f "$HOME_NIX" ]; then
echo "It looks like you don't have a ~/.config/home-manager/home.nix file yet."
echo "Nomarchy uses this file to manage your user-level packages and settings."
if gum confirm "Would you like to generate a starter home.nix?"; then
mkdir -p "$(dirname "$HOME_NIX")"
cat <<EOF > "$HOME_NIX"
{ pkgs, ... }:
{
# Nomarchy starter home.nix
# Add your user packages here.
home.packages = with pkgs; [
btop
fastfetch
chromium
# Add more packages here
];
# home.stateVersion = "25.11"; # Consult docs/MIGRATION.md if you change this
}
EOF
echo "Starter home.nix generated at $HOME_NIX"
fi
else
echo "Detected existing home.nix at $HOME_NIX. Skipping generation."
fi
# 5. Setup Local Repo (Crucial for nomarchy-env-update to work)
echo ""
echo "Step 5: Git Repository Check"
echo "Step 4: Git Repository Check"
echo "Nomarchy relies on a local git repository for declarative updates."
if [ ! -d "/etc/nixos/.git" ]; then
echo "Warning: /etc/nixos is not a git repository. Declarative updates might fail."
@@ -93,7 +72,7 @@ if [ ! -d "/etc/nixos/.git" ]; then
fi
fi
# 6. Success
# 5. Success
echo ""
echo "Applying all changes..."
nomarchy-env-update

View File

@@ -19,13 +19,17 @@
HARDWARE_DB=(
# Framework ---------------------------------------------------------------
# Module names follow nixos-hardware's actual attrs — for Framework 13
# the per-generation modules dropped the "13-" prefix.
"Framework|Laptop 16.*AMD|isFramework=true|framework-16-7040-amd"
"Framework|Laptop 13.*AMD Ryzen AI 300|isFramework=true|framework-13-amd-ai-300-series"
"Framework|Laptop 13.*AMD Ryzen 7040|isFramework=true|framework-13-7040-amd"
"Framework|Laptop 13.*13th Gen Intel|isFramework=true|framework-13-13th-gen-intel"
"Framework|Laptop 13.*12th Gen Intel|isFramework=true|framework-13-12th-gen-intel"
"Framework|Laptop 13.*11th Gen Intel|isFramework=true|framework-13-11th-gen-intel"
"Framework|Laptop \(13.*|isFramework=true|framework-13-11th-gen-intel"
"Framework|Laptop 16.*Ryzen AI 300|isFramework=true|framework-16-amd-ai-300-series"
"Framework|Laptop 13.*Ryzen AI 300|isFramework=true|framework-amd-ai-300-series"
"Framework|Laptop 13.*Ryzen 7040|isFramework=true|framework-13-7040-amd"
"Framework|Laptop 13.*Core Ultra|isFramework=true|framework-intel-core-ultra-series1"
"Framework|Laptop 13.*13th Gen Intel|isFramework=true|framework-13th-gen-intel"
"Framework|Laptop 13.*12th Gen Intel|isFramework=true|framework-12th-gen-intel"
"Framework|Laptop 13.*11th Gen Intel|isFramework=true|framework-11th-gen-intel"
"Framework|Laptop \(13.*|isFramework=true|framework"
# Dell XPS / Precision / Latitude ----------------------------------------
"Dell|XPS 15 9500|isXPS=true|dell-xps-15-9500"
@@ -36,40 +40,61 @@ HARDWARE_DB=(
"Dell|XPS 13 9380|isXPS=true|dell-xps-13-9380"
"Dell|XPS 13 7390|isXPS=true|dell-xps-13-7390"
"Dell|Precision 5530|_|dell-precision-5530"
"Dell|Latitude 7480|_|dell-latitude-7480"
"Dell|Latitude 5400|_|dell-latitude-5400"
"Dell|Latitude 7490|_|dell-latitude-7490"
"Dell|Latitude 7430|_|dell-latitude-7430"
"Dell|Latitude 7420|_|dell-latitude-7420"
# Lenovo ThinkPad --------------------------------------------------------
"LENOVO|ThinkPad X1 Carbon Gen 11|_|lenovo-thinkpad-x1-carbon-gen11"
"LENOVO|ThinkPad X1 Carbon Gen 10|_|lenovo-thinkpad-x1-carbon-gen10"
"LENOVO|ThinkPad X1 Carbon Gen 9|_|lenovo-thinkpad-x1-carbon-gen9"
# X1 Carbon: the per-gen modules are named "x1-Nth-gen", not
# "x1-carbon-genN" — both naming schemes appeared in nixos-hardware and
# the old DB references picked the wrong one. Carbon-specific quirks
# match the X1 modules because the X1 series IS the Carbon line.
"LENOVO|ThinkPad X1 Carbon Gen 11|_|lenovo-thinkpad-x1-11th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 10|_|lenovo-thinkpad-x1-10th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 9|_|lenovo-thinkpad-x1-9th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 7|_|lenovo-thinkpad-x1-7th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 6|_|lenovo-thinkpad-x1-6th-gen"
"LENOVO|ThinkPad X1 Extreme|_|lenovo-thinkpad-x1-extreme"
"LENOVO|ThinkPad X1 Nano|_|lenovo-thinkpad-x1-nano-gen1"
"LENOVO|ThinkPad T14 Gen 3|_|lenovo-thinkpad-t14-amd-gen3"
"LENOVO|ThinkPad T14 Gen 2|_|lenovo-thinkpad-t14-amd-gen2"
"LENOVO|ThinkPad T14 Gen 1|_|lenovo-thinkpad-t14-amd-gen1"
"LENOVO|ThinkPad T480|_|lenovo-thinkpad-t480"
"LENOVO|ThinkPad L13|_|lenovo-thinkpad-l13"
"LENOVO|ThinkPad P14s|_|lenovo-thinkpad-p14s"
"LENOVO|ThinkPad P14s.*Gen 5|_|lenovo-thinkpad-p14s-amd-gen5"
"LENOVO|ThinkPad P14s.*Gen 4|_|lenovo-thinkpad-p14s-amd-gen4"
"LENOVO|ThinkPad P14s.*Gen 3|_|lenovo-thinkpad-p14s-amd-gen3"
# Microsoft Surface ------------------------------------------------------
# nixos-hardware ships per-chip modules, not per-revision: Intel Surface
# Pros (6 through 10) all use `microsoft-surface-pro-intel`; AMD Surface
# Laptops use `microsoft-surface-laptop-amd`. Surface Pro 9 has its own
# variant for the SQ3 / ARM model — kept separate. Surface Book and
# Intel-based Surface Laptops have no dedicated module; we fall back to
# the common detection (chassis + cpu + gpu).
"Microsoft|Surface Pro 10|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 9|_|microsoft-surface-pro-9"
"Microsoft|Surface Pro 8|_|microsoft-surface-pro-8"
"Microsoft|Surface Pro 7|_|microsoft-surface-pro-7"
"Microsoft|Surface Pro 6|_|microsoft-surface-pro-6"
"Microsoft|Surface Laptop 5|_|microsoft-surface-laptop-5"
"Microsoft|Surface Laptop 4|_|microsoft-surface-laptop-4"
"Microsoft|Surface Laptop 3|_|microsoft-surface-laptop-3"
"Microsoft|Surface Pro 8|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 7|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 6|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 3|_|microsoft-surface-pro-3"
"Microsoft|Surface Laptop.*Ryzen|_|microsoft-surface-laptop-amd"
"Microsoft|Surface Go|_|microsoft-surface-go"
"Microsoft|Surface Book 3|_|microsoft-surface-book-3"
"Microsoft|Surface Book 2|_|microsoft-surface-book-2"
# ASUS ROG / Zephyrus ----------------------------------------------------
"ASUS.*|ROG Zephyrus G14.*2024|_|asus-zephyrus-ga403"
"ASUS.*|ROG Zephyrus G14.*2023|_|asus-zephyrus-ga402"
"ASUS.*|ROG Zephyrus G14.*2021|_|asus-zephyrus-ga401"
# ASUS ROG Ally / Zephyrus / Strix --------------------------------------
# Ally is the handheld (Z1 Extreme / Z1). RC71L is the original Ally,
# RC72LA is the Ally X — both share the same nixos-hardware module
# currently.
"ASUS.*|RC71L|_|asus-ally-rc71l"
"ASUS.*|RC72LA|_|asus-ally-rc71l"
"ASUS.*|ROG Ally|_|asus-ally-rc71l"
"ASUS.*|ROG Zephyrus G14.*GA402X|_|asus-zephyrus-ga402x"
"ASUS.*|ROG Zephyrus G14.*GA402|_|asus-zephyrus-ga402"
"ASUS.*|ROG Zephyrus G14.*GA401|_|asus-zephyrus-ga401"
"ASUS.*|ROG Zephyrus G15|_|asus-zephyrus-ga503"
"ASUS.*|ROG Strix G15|_|asus-rog-strix-g513"
"ASUS.*|Zenbook UX|_|asus-zenbook-ux"
"ASUS.*|ROG Zephyrus.*GU603|_|asus-zephyrus-gu603h"
"ASUS.*|ROG Strix G513|_|asus-rog-strix-g513im"
"ASUS.*|ROG Strix G533|_|asus-rog-strix-g533zw"
# Apple (T2 Intel; M-series falls back to asahi elsewhere) ---------------
"Apple.*|MacBookPro15|isT2Mac=true|apple-t2"
@@ -86,9 +111,14 @@ HARDWARE_DB=(
"System76|Galago Pro.*|_|system76"
"System76|Pangolin.*|_|system76"
# Raspberry Pi (ARM) — listed for completeness; installer is x86_64 only -
# "Raspberry Pi|Raspberry Pi 5|_|raspberry-pi-5"
# "Raspberry Pi|Raspberry Pi 4|_|raspberry-pi-4"
# Devices nixos-hardware doesn't cover (yet) -----------------------------
# Listed here so future contributors know they're known-unsupported, not
# accidentally missing:
# - Valve Steam Deck (Galileo / Jupiter): try Jovian-NixOS as a flake
# input instead — separate ecosystem, not in nixos-hardware.
# - Snapdragon X laptops (Surface Pro 11, Lenovo Yoga Slim 7x, …):
# aarch64-only and the Nomarchy installer is x86_64 only.
# - Raspberry Pi (ARM): same — installer is x86_64 only.
)
# ----------------------------------------------------------------------------

View File

@@ -74,6 +74,9 @@ Usage: install.sh [--dry-run] [--resume] [-h|--help]
Doesn't touch the disk, doesn't run nixos-install.
--resume Reuse answers from a previous interrupted run
(saved at $STATE_FILE — passwords excluded).
NOTE: the live ISO uses tmpfs, so the state file is lost
on reboot. --resume only works within the same live-ISO
session as the original interrupted run.
-h, --help Print this message.
USAGE
}
@@ -102,19 +105,74 @@ parse_args() {
# will silently install a system with an empty password hash and lock the
# user out. Keep the guard checking the hash.
save_state() {
declare -p \
TARGET_DRIVE USERNAME HOSTNAME TIMEZONE \
KEYMAP_LAYOUT KEYMAP_VARIANT LOCALE FORM_FACTOR \
ENABLE_IMPERMANENCE HARDWARE_MODULES NOMARCHY_HW_OPTS \
SELECTED_PROFILES NOMARCHY_REV \
> "$STATE_FILE"
# The leading timestamp lets --resume surface "how old is this state?"
# and is parsed back via NOMARCHY_INSTALL_STATE_SAVED_AT.
{
echo "NOMARCHY_INSTALL_STATE_SAVED_AT=\"$(date -Iseconds)\""
declare -p \
TARGET_DRIVE USERNAME HOSTNAME TIMEZONE \
KEYMAP_LAYOUT KEYMAP_VARIANT LOCALE FORM_FACTOR \
ENABLE_IMPERMANENCE HARDWARE_MODULES NOMARCHY_HW_OPTS \
SELECTED_PROFILES NOMARCHY_REV
} > "$STATE_FILE"
}
# Pretty-print "X minutes/hours/days ago" from an ISO-8601 timestamp.
# Falls back to the raw string if `date -d` can't parse it (defensive —
# the timestamp is always produced by `date -Iseconds` above, but we
# don't want a stale state file to crash --resume).
format_age() {
local saved="$1" saved_epoch now_epoch diff
saved_epoch=$(date -d "$saved" +%s 2>/dev/null) || { echo "$saved"; return; }
now_epoch=$(date +%s)
diff=$(( now_epoch - saved_epoch ))
if (( diff < 60 )); then echo "${diff}s ago"
elif (( diff < 3600 )); then echo "$((diff / 60))m ago"
elif (( diff < 86400 )); then echo "$((diff / 3600))h $((diff % 3600 / 60))m ago"
else echo "$((diff / 86400))d ago"
fi
}
load_state() {
if [[ "$RESUME" == "true" ]] && [[ -f "$STATE_FILE" ]]; then
# shellcheck disable=SC1090
source "$STATE_FILE"
info "Resumed from $STATE_FILE"
if [[ "$RESUME" != "true" ]]; then
return
fi
# --resume with no state file is almost always operator error — the
# most common cause is "rebooted the live ISO and forgot tmpfs eats
# /tmp/". Fail loudly so the user doesn't sit through a fresh prompt
# cycle thinking it was resumed.
if [[ ! -f "$STATE_FILE" ]]; then
error "--resume was passed but no saved state exists at $STATE_FILE."
info "The live ISO uses tmpfs — saved state doesn't survive a reboot."
info "Re-run install.sh without --resume to start fresh."
exit 1
fi
# shellcheck disable=SC1090
source "$STATE_FILE"
# If the saved target drive isn't visible right now, every later
# disk-phase step will fail with cryptic errors. Catch it here.
# Live ISOs frequently get their non-boot USB sticks unplugged
# between sessions, and dev hosts sometimes have non-deterministic
# /dev/sdX numbering.
if [[ -n "${TARGET_DRIVE:-}" ]] && [[ ! -b "$TARGET_DRIVE" ]]; then
error "Saved target drive $TARGET_DRIVE is no longer a block device."
info "The drive may have been unplugged or renamed since the saved run."
info "Delete $STATE_FILE and re-run without --resume."
exit 1
fi
# Show what we're resuming into so the user can Ctrl-C if they're on
# the wrong host before any password / disk-wipe prompts fire.
local age="unknown age"
if [[ -n "${NOMARCHY_INSTALL_STATE_SAVED_AT:-}" ]]; then
age=$(format_age "$NOMARCHY_INSTALL_STATE_SAVED_AT")
fi
info "Resumed from $STATE_FILE (saved $age)"
if [[ -n "${USERNAME:-}" || -n "${HOSTNAME:-}" || -n "${TARGET_DRIVE:-}" ]]; then
info " Target: ${TARGET_DRIVE:-?}${USERNAME:-?} @ ${HOSTNAME:-?}"
fi
}
@@ -1292,7 +1350,7 @@ execute_installation() {
mkdir -p /mnt/persist/etc
mv /mnt/etc/nixos /mnt/persist/etc/
mkdir -p /mnt/etc
ln -s /persist/etc/nixos /mnt/etc/nixos
ln -s ../persist/etc/nixos /mnt/etc/nixos
success "Impermanence configured"
fi
@@ -1401,7 +1459,7 @@ generate_flake_config() {
if (( ${#_drives[@]} > 1 )); then
_main_luks_name="crypted_main"
fi
impermanence_opt=$'nomarchy.system.impermanence.enable = true;\n nomarchy.system.impermanence.mainLuksName = "'"$_main_luks_name"$'";'
impermanence_opt=$'nomarchy.system.impermanence.enable = true;\n nomarchy.system.impermanence.mainLuksName = "'"$_main_luks_name"$'";\n nomarchy.system.impermanence.user = "'"$USERNAME"$'";'
fi
local PROFILE_SYSTEM_OPTS=""
@@ -1741,30 +1799,29 @@ EOF
}
EOF
# state.json — consumed by core/system/state.nix at every nixos-rebuild and
# mutated by toggle scripts (nomarchy-tz-select, nomarchy-setup-fido2,
# state.json — consumed by core/system/state.nix at every nixos-rebuild
# and mutated by toggle scripts (nomarchy-tz-select, nomarchy-setup-fido2,
# nomarchy-toggle-hybrid-gpu, nomarchy-wifi-powersave, ...). Those scripts
# `jq` the file in place and fail hard if it doesn't exist or isn't valid
# JSON, so a fresh install MUST ship one. Shape must match lib/state-schema.nix.
# Quoted heredoc — no shell expansion except the explicit $TIMEZONE below.
# `jq` the file in place and fail hard if it doesn't exist or isn't
# valid JSON, so a fresh install MUST ship one.
#
# Source of truth: lib/state-schema.nix. We eval its `system` block and
# overlay the installer-chosen timezone, so this generator no longer
# drifts from the schema or the *.nix consumers — the previous heredoc
# was the last source-of-truth split after the state centralization.
# Adding a new default in the schema now reaches the installer without
# any further plumbing.
local _state_tz="${TIMEZONE:-UTC}"
cat > /mnt/etc/nixos/state.json <<JSON_EOF
{
"theme": "nord",
"timezone": "${_state_tz}",
"dns": "DHCP",
"customDns": [],
"wifi": {
"powersave": true
},
"features": {
"fingerprint": false,
"fido2": false,
"hybridGPU": false,
"makima": false
}
}
JSON_EOF
nix --extra-experimental-features 'nix-command flakes' eval \
--impure --raw \
--expr "
let
flake = builtins.getFlake \"$NOMARCHY_REPO\";
lib = flake.inputs.nixpkgs.lib;
schema = import \"$NOMARCHY_REPO/lib/state-schema.nix\" { inherit lib; };
state = schema.system // { timezone = \"$_state_tz\"; };
in builtins.toJSON state
" | nrun jq '.' > /mnt/etc/nixos/state.json
}
# ============================================================================

View File

@@ -98,7 +98,6 @@ let
in {
inherit
palettes
readState
readHomeState
readSystemState
resolveWallpaper

View File

@@ -1,12 +1,20 @@
# Nomarchy State Schema
# Defines the complete state shape with defaults for both home and system state
#
# Defines the default values for every state.json field that's consumed by a
# Nix option. Read by core/{system,home}/options.nix (for `default = …`) and
# by core/{system,home}/state.nix (for `or` fallbacks).
#
# state.json may also hold runtime-only fields that aren't declared here —
# notably `welcome_done`, managed by `nomarchy-welcome`. Those are intentionally
# off-schema because no Nix option reads them; the schema is the "consumed by
# Nix" surface, not the full state.json shape.
{ lib }:
{
# Home state defaults (user preferences)
home = {
# Theme and appearance
theme = "nord";
theme = "summer-night";
wallpaper = "";
font = "JetBrainsMono Nerd Font";
panelPosition = "top";
@@ -18,7 +26,6 @@
idle = true;
nightlight = false;
waybar = true;
skipVsCodeTheme = false;
# Hyprland window manager settings
hyprland = {
@@ -31,7 +38,7 @@
# System state defaults (system-level configuration)
system = {
# Theme (can differ from home for system-level theming)
theme = "nord";
theme = "summer-night";
# Timezone
timezone = "UTC";
@@ -50,18 +57,7 @@
fingerprint = false;
fido2 = false;
hybridGPU = false;
makima = false;
};
};
# Get a value from state with fallback to default
getWithDefault = state: path: default:
let
pathList = lib.splitString "." path;
getValue = obj: remaining:
if remaining == [] then obj
else if builtins.isAttrs obj && builtins.hasAttr (builtins.head remaining) obj
then getValue obj.${builtins.head remaining} (builtins.tail remaining)
else default;
in getValue state pathList;
}

View File

@@ -33,6 +33,62 @@ in
@define-color accent #${palette.base0D};
'';
# Per-palette kitty colors. features/apps/kitty/config/kitty.conf includes
# this file; without it the include silently failed and kitty stayed on
# default colors for every palette (Stylix's kitty target only kicks in
# when programs.kitty.enable is set, which the module doesn't use).
xdg.configFile."nomarchy/current/theme/kitty.conf".text = ''
background #${palette.base00}
foreground #${palette.base05}
cursor #${palette.base05}
selection_background #${palette.base02}
selection_foreground #${palette.base05}
color0 #${palette.base01}
color1 #${palette.base08}
color2 #${palette.base0B}
color3 #${palette.base0A}
color4 #${palette.base0D}
color5 #${palette.base0E}
color6 #${palette.base0C}
color7 #${palette.base04}
color8 #${palette.base03}
color9 #${palette.base08}
color10 #${palette.base0B}
color11 #${palette.base0A}
color12 #${palette.base0D}
color13 #${palette.base0E}
color14 #${palette.base0C}
color15 #${palette.base07}
'';
# Per-palette ghostty colors. features/apps/ghostty/config/config uses an
# optional include (?-prefix) of this file; without it the include was
# silently skipped and ghostty rendered with its built-in defaults across
# every palette. ghostty has no Stylix target.
xdg.configFile."nomarchy/current/theme/ghostty.conf".text = ''
background = ${palette.base00}
foreground = ${palette.base05}
cursor-color = ${palette.base05}
selection-background = ${palette.base02}
selection-foreground = ${palette.base05}
palette = 0=#${palette.base01}
palette = 1=#${palette.base08}
palette = 2=#${palette.base0B}
palette = 3=#${palette.base0A}
palette = 4=#${palette.base0D}
palette = 5=#${palette.base0E}
palette = 6=#${palette.base0C}
palette = 7=#${palette.base04}
palette = 8=#${palette.base03}
palette = 9=#${palette.base08}
palette = 10=#${palette.base0B}
palette = 11=#${palette.base0A}
palette = 12=#${palette.base0D}
palette = 13=#${palette.base0E}
palette = 14=#${palette.base0C}
palette = 15=#${palette.base07}
'';
# Ensure theme-specific hyprland config exists
# Lookup priority: palette apps/ > feature themes/ > nord fallback
xdg.configFile."nomarchy/current/theme/apps/hyprland.conf" = lib.mkIf (!hasHyprlandConf) {
@@ -83,6 +139,12 @@ in
path = ../palettes;
};
# Expose all theme templates to the system via local share
xdg.dataFile."nomarchy/templates".source = builtins.path {
name = "nomarchy-templates";
path = ../templates;
};
# Nautilus python extensions
xdg.dataFile."nautilus-python/extensions/localsend.py".source = ../../core/home/config/nautilus-python/extensions/localsend.py;
}

View File

@@ -54,31 +54,16 @@ in
};
apps = {
# waybar, kitty, alacritty, and mako are intentionally absent. Waybar
# themes inline in features/desktop/waybar via colorScheme; kitty and
# alacritty are themed by stylix targets (themes/engine/stylix.nix); mako
# has no theme integration yet. Only btop is loaded from the active
# theme's apps/ directory.
btop = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load btop theme from active theme.";
};
waybar = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load waybar CSS from active theme.";
};
mako = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load mako config from active theme.";
};
kitty = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load kitty config from active theme.";
};
alacritty = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load alacritty config from active theme.";
};
};
};

View File

@@ -40,8 +40,7 @@ let
for file in $out/bin/*; do
if [ -f "$file" ]; then
wrapProgram "$file" \
--prefix PATH : "$deps" \
--set NOMARCHY_PATH "/etc/nixos"
--prefix PATH : "$deps"
fi
done
'';

View File

@@ -2,8 +2,8 @@
set -e
{
find ~/.config/nomarchy/themes/ -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -printf '%f\n'
find "$NOMARCHY_PATH/themes/palettes/" -mindepth 1 -maxdepth 1 -type d -printf '%f\n'
find ~/.config/nomarchy/themes/ -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -printf '%f\n' 2>/dev/null || true
find ~/.local/share/nomarchy/themes/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null || true
} | sort -u | while read -r name; do
echo "$name" | sed -E 's/(^|-)([a-z])/\1\u\2/g; s/-/ /g'
done

View File

@@ -1,7 +1,7 @@
#!/bin/bash
set -e
TEMPLATES_DIR="$NOMARCHY_PATH/themes/templates"
TEMPLATES_DIR="$HOME/.local/share/nomarchy/templates"
USER_TEMPLATES_DIR="$HOME/.config/nomarchy/themes/templates"
NEXT_THEME_DIR="$HOME/.config/nomarchy/current/theme"
COLORS_FILE="$NEXT_THEME_DIR/colors.toml"

View File

@@ -27,8 +27,12 @@ in
services.displayManager.defaultSession = lib.mkDefault "hyprland-uwsm";
# autoLogin defaults off so hand-migrated configs (no installer-written
# username) don't try to log in as a nonexistent "nomarchy" user. The
# installer-generated system.nix sets both `enable = true;` and
# `user = "$USERNAME";` at normal priority, overriding these defaults.
services.displayManager.autoLogin = {
enable = lib.mkDefault true;
enable = lib.mkDefault false;
user = lib.mkDefault "nomarchy";
};

View File

@@ -1,5 +0,0 @@
include=~/.config/nomarchy/default/mako/core.ini
text-color={{ foreground }}
border-color={{ accent }}
background-color={{ background }}