Compare commits

...

32 Commits

Author SHA1 Message Date
Bernardo Magri
83218a01f3 docs: add "Working on Nomarchy" orientation guide
Some checks failed
Check / eval-and-lint (push) Failing after 6m21s
A short, task-indexed guide for navigating the repo: the core/features/themes
mental model, the two app-config namespaces, the four script locations (incl.
themes/engine/scripts/), the build/test loop, how theming works, and a
Walker+elephant deep-dive covering the dmenu vs custom-provider menu paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:32:24 +01:00
Bernardo Magri
9fe6d9e405 fix(walker): don't spawn a duplicate elephant alongside the service
nomarchy-launch-walker guarded its manual elephant start with
`pgrep -x elephant`, which never matches the Home Manager service's actual
process name `.elephant-wrapped` (Linux truncates comm to 15 chars). So every
menu invocation spawned a second elephant that races elephant.service for the
socket — a latent source of menus intermittently coming up empty.

Gate on `systemctl --user is-active elephant.service` first, with a working
`pgrep -f elephant-wrapped` fallback for non-service setups; do the same for
walker.service. Verified in a headless system.build.vm: opening the theme menu
now keeps the elephant process count at 1 (was 2) and the menu still renders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:32:12 +01:00
Bernardo Magri
8a302e8771 chore: drop omarchy pkg-* compatibility shims
Some checks failed
Check / eval-and-lint (push) Failing after 6m15s
These existed only to ease muscle-memory from Arch-based distros and add
no behavior on a Nix-native system:

- nomarchy-pkg-install — alias for nomarchy-pkg-add
- nomarchy-pkg-drop    — alias for nomarchy-pkg-remove
- nomarchy-pkg-aur-add — stub that only printed "AUR doesn't exist on NixOS"

The single code caller (nomarchy-voxtype-remove) was removed in the prior
commit, so the shims have no remaining references. SKILL.md updated to use
nomarchy-pkg-add and to point at `nix search nixpkgs` instead of the AUR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:52:19 +01:00
Bernardo Magri
698d8593cd feat: remove unused voxtype dictation feature
Voxtype voice-typing was a full feature (5 scripts, a waybar widget, a
keybind, and a NixOS option) that this install does not use. The
nomarchy.system.voxtype option had no consumer — nothing read it — so the
widget and SUPER+CTRL+X bind were never actually gated by it.

Removes the scripts, default config, the SUPER+CTRL+X bind, the waybar
module (main + summer-night theme + css), the dead option, the installer
comment, and the docs. SCRIPTS.md/KEYBINDINGS.md regenerated; OPTIONS.md
and SKILL.md hand-updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:51:46 +01:00
Bernardo Magri
4b509c271a fix(scripts): remove stale duplicate nomarchy-update
Some checks failed
Check / eval-and-lint (push) Failing after 6m14s
Two scripts named nomarchy-update shipped from different derivations
(core/system/scripts + features/scripts/utils), both landing on PATH so
which one ran depended on PATH ordering. The core/system one never stripped
the install-time `?rev=` pin and rebuilt a nonexistent `#default` attr,
silently keeping installs frozen instead of following rolling main. Keep the
features/scripts/utils version (unpins, queries upstream HEAD, calls
nomarchy-sys-update) and drop the stale one. Regenerated docs/SCRIPTS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:55:04 +01:00
Bernardo Magri
2bdcc0aba0 fix(live): enable VM guest agents so Hyprland boots at full resolution
The nomarchy-live ISO imported ./core and hosts/nomarchy-live.nix but not
core/system/vm-guest.nix, which is the only place spice-vdagentd/qemuGuest
were enabled. Without those agents a VM's virtio-gpu only advertises its
1024x768 fallback mode, so Hyprland's `monitor = , highres, auto` rule picks
that as the highest mode and the live session boots tiny.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:55:04 +01:00
Bernardo Magri
27821c270f fix(impermanence): mark /persist and /home neededForBoot
Some checks failed
Check / eval-and-lint (push) Failing after 5m56s
The impermanence module asserts that the persistent-storage volume and
every volume it bind-mounts into are available in early boot. Persisted
user dirs land under /home, so both /persist and the @home subvolume
must have neededForBoot = true. disko and nixos-generate-config leave it
at the default (false), so a fresh install with impermanence enabled
aborted with:

    All filesystems used for persistent storage must have the option
    "neededForBoot" set to true.
    Please fix the following filesystems: /persist /home

Declare neededForBoot in the module that imposes the requirement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 19:34:04 +01:00
Bernardo Magri
a84e903ae1 fix(live): allow empty-password hyprlock unlock on live ISO
pam_unix rejects empty passwords by default, so if hypridle locked the
live session there was nothing the user could type to unlock it. Set
allowNullPassword on the hyprlock PAM service so a bare Enter dismisses
the locker, matching the empty live account passwords. Scoped to the
live host so installed systems still reject empty unlocks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 19:24:42 +01:00
Bernardo Magri
5ea369d73c fix(installer): declare extra LUKS devices for multi-disk boot
All checks were successful
Check / eval-and-lint (push) Successful in 6m51s
A multi-disk install builds one LUKS container per drive joined into a
single multi-device BTRFS (-d single -m raid1). nixos-generate-config
only emits boot.initrd.luks.devices for the main drive — every subvolume
mounts through /dev/mapper/crypted_main, so the other members are
invisible to it. The installed system then unlocked only the main drive,
the array never assembled, and boot hung. Invisible single-disk, fatal
multi-disk (matches a real-hardware report).

generate_flake_config now writes, into hardware-selection.nix, a
boot.initrd.luks.devices entry for each extra drive (UUID resolved via
disko's partlabel; mapper name mirrors disko-config.nix's sanitize) and
re-asserts the x-systemd.requires= mount options disko sets but which
nixos-generate-config strips.

Verified end-to-end in a 3-disk UEFI QEMU VM: reproduced the bug, applied
the fix, ran a real nixos-install onto the array, and rebooted from the
disks — one passphrase unlocked all three containers, the 3-device BTRFS
assembled, and the system reached login.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:50:10 +01:00
Bernardo Magri
fdd74a0e4f fix(installer): resolve /etc/nomarchy symlink before getFlake
`generate_state` loads `nixpkgs.lib` for the state-schema eval via
`builtins.getFlake "$NOMARCHY_REPO"`. On the live ISO that path is a
symlink chain (/etc/nomarchy -> /etc/static/nomarchy -> /nix/store/…-source),
and Nix 2.31+ rejects getFlake on a symlink ("path '…-source' is a
symlink"), aborting every real-hardware install right after hardware
config generation — single or multi disk.

Resolve the repo to its real store directory with `realpath` at
detection so getFlake receives a plain directory. The dev-checkout
branch already used realpath, so only the live-ISO path was affected.
Reproduced and fixed in a live VM (Nix 2.31.4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:49:43 +01:00
Bernardo Magri
431dd98967 feat(theme): implement declarative Stylix-driven theming for Walker
All checks were successful
Check / eval-and-lint (push) Successful in 6m58s
2026-06-01 21:09:54 +01:00
Bernardo Magri
fa71e03f88 docs: move remaining Ironclad verifications to Shipped
All checks were successful
Check / eval-and-lint (push) Successful in 6m45s
2026-06-01 21:00:09 +01:00
Bernardo Magri
e3002d49ad fix(waybar): ensure update module is visible in summer-night theme
All checks were successful
Check / eval-and-lint (push) Successful in 6m37s
2026-06-01 20:36:04 +01:00
Bernardo Magri
dc7a3e2a29 feat(update): add interactive confirmation prompt to nomarchy-update and expose in all themes
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-06-01 20:35:06 +01:00
Bernardo Magri
dd72110b9a docs: finalize roadmap entries for flake updates and offline resilience
All checks were successful
Check / eval-and-lint (push) Successful in 6m43s
2026-06-01 20:27:25 +01:00
Bernardo Magri
1e6becd42d fix(scripts): improve offline resilience and restore missing update scripts
All checks were successful
Check / eval-and-lint (push) Successful in 7m5s
2026-06-01 20:07:08 +01:00
Bernardo Magri
6ed2ca3d78 fix(scripts): resolve race condition in state-sync and mark stress test as complete
All checks were successful
Check / eval-and-lint (push) Successful in 7m9s
2026-06-01 18:48:17 +01:00
Bernardo Magri
d242d1ea88 fix(waybar): reconcile tooltips with keybindings and fix stale references
Some checks failed
Check / eval-and-lint (push) Failing after 1m52s
2026-05-31 21:59:08 +01:00
Bernardo Magri
3a0164feb7 docs: mark Round 2 Interactive Script Audit as complete
All checks were successful
Check / eval-and-lint (push) Successful in 6m43s
2026-05-31 21:45:41 +01:00
Bernardo Magri
8dc6ba0a21 docs: mark App Integration Polish as complete
All checks were successful
Check / eval-and-lint (push) Successful in 6m38s
2026-05-31 20:59:26 +01:00
Bernardo Magri
5d558a3cc6 fix(system): enable Polkit for Thunar drive management
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:59:04 +01:00
Bernardo Magri
ea34a7ac1f fix(apps): improve Thunar and Mako integration
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:58:10 +01:00
Bernardo Magri
878c11bd41 docs: add RUNTIME_HOOKS.md and link from README
All checks were successful
Check / eval-and-lint (push) Successful in 6m35s
2026-05-31 20:35:58 +01:00
Bernardo Magri
3a42b664a1 feat: wire up post-update and post-install hooks
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:34:01 +01:00
Bernardo Magri
1301873bc0 docs: finalize declarative-state migration in roadmap
Some checks failed
Check / eval-and-lint (push) Failing after 6m45s
2026-05-31 20:09:31 +01:00
Bernardo Magri
dc3346bc55 feat: implement hybrid declarative state with automatic Nix sync
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 20:09:12 +01:00
Bernardo Magri
624023c1d5 docs: move declarative-state migration batch 1 and monitor scaling to Shipped
Some checks failed
Check / eval-and-lint (push) Failing after 6m42s
2026-05-31 19:33:57 +01:00
Bernardo Magri
130ae80f3e feat: migrate theme and font to declarative Nix options
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 19:33:33 +01:00
Bernardo Magri
a2649728d3 feat(hyprland): implement declarative monitor scaling and add scale picker to welcome wizard
Some checks failed
Check / eval-and-lint (push) Failing after 6m36s
2026-05-31 19:21:52 +01:00
Bernardo Magri
8911ffa4e8 feat(calendar): ship calcurse and wire waybar clock clicks
Some checks failed
Check / eval-and-lint (push) Failing after 6m22s
2026-05-31 16:43:47 +01:00
Bernardo Magri
395c839ed0 fix(waybar): retarget broken clicks in summer-day/night themes to nomarchy scripts
Some checks failed
Check / eval-and-lint (push) Has been cancelled
2026-05-31 16:42:31 +01:00
Bernardo Magri
a9cc39f3fe fix(input-method): update fcitx5-chinese-addons to kdePackages.fcitx5-chinese-addons
All checks were successful
Check / eval-and-lint (push) Successful in 6m51s
2026-05-31 16:12:17 +01:00
68 changed files with 945 additions and 575 deletions

View File

@@ -84,7 +84,7 @@ Add user-level packages, aliases, and dotfiles here.
nomarchy.home.terminal = "kitty";
```
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). Hit a rebuild error? Check [Troubleshooting](docs/TROUBLESHOOTING.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). For custom automation, see [Runtime Hooks](docs/HOOKS.md). Hit a rebuild error? Check [Troubleshooting](docs/TROUBLESHOOTING.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
### Applying Changes
After editing your files, apply them instantly. **IMPORTANT:** Nomarchy requires the `--impure` flag for evaluation. You **MUST** use the following aliases rather than standard NixOS commands:

View File

@@ -109,7 +109,7 @@ cat $(which nomarchy-theme-set)
| `nomarchy-install-*` | Install optional software | `nomarchy-install-docker-dbs` |
| `nomarchy-launch-*` | Launch apps | `nomarchy-launch-browser` |
| `nomarchy-cmd-*` | System commands | `nomarchy-cmd-screenshot` |
| `nomarchy-pkg-*` | Package management | `nomarchy-pkg-install <pkg>` |
| `nomarchy-pkg-*` | Package management | `nomarchy-pkg-add <pkg>` |
| `nomarchy-setup-*` | Initial setup tasks | `nomarchy-setup-fingerprint` |
| `nomarchy-update-*` | System updates | `nomarchy-update` |
@@ -334,16 +334,6 @@ nomarchy-tui-remove-all # Bulk-remove every TUI launcher
Both families write `.desktop` files into `~/.local/share/applications/` so they appear in the app launcher (walker / rofi).
### Voice dictation (Voxtype)
```bash
nomarchy-voxtype-install # Install Voxtype + AI model (~150MB)
nomarchy-voxtype-remove # Uninstall Voxtype
nomarchy-voxtype-status # Running state (also shown in waybar)
```
Toggle dictation with `SUPER+CTRL+X` after install.
### Virtualization
```bash
@@ -387,7 +377,7 @@ When user requests system changes:
2. **Is it a config edit?** Edit in `~/.config/`, never `~/.local/share/nomarchy/`
3. **Is it a theme customization?** Create a NEW custom theme directory
4. **Is it automation?** Use hooks in `~/.config/nomarchy/hooks/`
5. **Is it a package install?** Use `nomarchy-pkg-add` (or `nomarchy-pkg-aur-add` for AUR-only packages)
5. **Is it a package install?** Use `nomarchy-pkg-add` (adds to `user-packages.json`; rebuild applies it). The AUR does not exist on NixOS — search nixpkgs with `nix search nixpkgs <name>`.
6. **Unsure if command exists?** Search with `compgen -c | grep nomarchy`
## Out of Scope

View File

@@ -51,9 +51,6 @@ bindd = SUPER CTRL, B, Bluetooth controls, exec, nomarchy-launch-bluetooth
bindd = SUPER CTRL, W, Wifi controls, exec, nomarchy-launch-wifi
bindd = SUPER CTRL, T, Activity, exec, nomarchy-launch-tui btop
# Dictation
bindd = SUPER CTRL, X, Toggle dictation, exec, voxtype record toggle
# Zoom
bindd = SUPER CTRL, Z, Zoom in, exec, hyprctl keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor -j | jq '.float + 1')
bindd = SUPER CTRL ALT, Z, Reset zoom, exec, hyprctl keyword cursor:zoom_factor 1

View File

@@ -22,12 +22,15 @@ layer=overlay
[summary~="Setup Wi-Fi"]
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Setup Wi-Fi"; nomarchy-launch-wifi'
on-click=exec sh -c 'nomarchy-notification-dismiss "Setup Wi-Fi"; nomarchy-launch-wifi'
[summary~="Update System"]
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Update System"; nomarchy-launch-floating-terminal-with-presentation nomarchy-update'
on-click=exec sh -c 'nomarchy-notification-dismiss "Update System"; nomarchy-launch-floating-terminal-with-presentation nomarchy-update'
[summary~="Learn Keybindings"]
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Learn Keybindings"; nomarchy-menu-keybindings'
on-click=exec sh -c 'nomarchy-notification-dismiss "Learn Keybindings"; nomarchy-menu-keybindings'
[summary~="Screenshot copied & saved"]
max-icon-size=80

View File

@@ -1,97 +0,0 @@
# Voxtype Configuration
#
# Location: ~/.config/voxtype/config.toml
# All settings can be overridden via CLI flags
#
# State file for external integrations (Waybar, polybar, etc.)
# Use "auto" for default location ($XDG_RUNTIME_DIR/voxtype/state),
# a custom path, or "disabled" to turn off. The daemon writes state
# ("idle", "recording", "transcribing") to this file whenever it changes.
# Required for `voxtype record toggle` and `voxtype status` commands.
state_file = "auto"
[hotkey]
# Hotkey is configured in Hyprland. Default is Super + Ctrl + X
enabled = false
[audio]
# Audio input device ("default" uses system default)
# List devices with: pactl list sources short
device = "default"
# Sample rate in Hz (whisper expects 16000)
sample_rate = 16000
# Maximum recording duration in seconds (safety limit)
max_duration_secs = 60
# [audio.feedback]
# Enable audio feedback sounds (beeps when recording starts/stops)
# enabled = true
#
# Sound theme: "default", "subtle", "mechanical", or path to custom theme directory
# theme = "default"
#
# Volume level (0.0 to 1.0)
# volume = 0.7
[whisper]
# Model to use for transcription
# Options: tiny, tiny.en, base, base.en, small, small.en, medium, medium.en, large-v3, large-v3-turbo
# .en models are English-only but faster and more accurate for English
# large-v3-turbo is faster than large-v3 with minimal accuracy loss (recommended for GPU)
# Or provide absolute path to a custom .bin model file
model = "base.en"
# Language for transcription
# Use "en" for English, "auto" for auto-detection
# See: https://github.com/openai/whisper#available-models-and-languages
language = "en"
# Translate non-English speech to English
translate = false
# Number of CPU threads for inference (omit for auto-detection)
# threads = 4
[output]
# Primary output mode: "type" or "clipboard"
# - type: Simulates keyboard input at cursor position (requires ydotool)
# - clipboard: Copies text to clipboard (requires wl-copy)
mode = "type"
# Fall back to clipboard if typing fails
fallback_to_clipboard = true
# Delay between typed characters in milliseconds
# 0 = fastest possible, increase if characters are dropped
type_delay_ms = 1
# Post-processing command (optional)
# Pipe transcribed text through an external command for cleanup before output.
# The command receives text on stdin and outputs processed text on stdout.
# Useful for LLM-based text cleanup, grammar correction, filler word removal.
# On any failure (timeout, error), falls back to original transcription.
#
# [output.post_process]
# command = "ollama run llama3.2:1b 'Clean up this dictation. Fix grammar, remove filler words. Output only the cleaned text:'"
# timeout_ms = 30000 # 30 second timeout (generous for LLM)
[output.notification]
# Show notification when recording starts (hotkey pressed)
on_recording_start = false
# Show notification when recording stops (transcription beginning)
on_recording_stop = false
# Show notification with transcribed text after transcription completes
on_transcription = false
# [text]
# Text processing options (word replacements, spoken punctuation)
#
# Enable spoken punctuation conversion (e.g., say "period" to get ".")
# spoken_punctuation = false
#
# Custom word replacements (case-insensitive)
# replacements = { "hyperwhisper" = "hyprwhspr" }

View File

@@ -0,0 +1,9 @@
#!/bin/bash
# This hook is called after the nomarchy-welcome wizard finishes successfully.
# Use it to run any custom first-boot logic (e.g. cloning your dotfiles repo,
# setting up SSH keys, or launching specific initial apps).
# Example: Open the manual and show a notification
# nomarchy-manual
# notify-send "Welcome home" "Your Nomarchy system is ready to use."

View File

@@ -42,7 +42,11 @@ in
theme = lib.mkOption {
type = lib.types.str;
default = schema.home.theme;
description = "System theme name.";
description = ''
System theme name. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the entire desktop.
'';
};
formFactor = lib.mkOption {
type = lib.types.enum [ "laptop" "desktop" ];
@@ -84,35 +88,56 @@ in
wallpaper = lib.mkOption {
type = lib.types.str;
default = schema.home.wallpaper;
description = "System wallpaper path.";
description = ''
System wallpaper path. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the background.
'';
};
panelPosition = lib.mkOption {
type = lib.types.enum [ "top" "bottom" ];
default = schema.home.panelPosition;
description = "Waybar panel position.";
description = ''
Waybar panel position. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the UI.
'';
};
hyprland = {
gaps_in = lib.mkOption {
type = lib.types.int;
default = schema.home.hyprland.gaps_in;
description = "Inner gaps for Hyprland.";
description = "Inner gaps for Hyprland. Driven by the declarative state.";
};
gaps_out = lib.mkOption {
type = lib.types.int;
default = schema.home.hyprland.gaps_out;
description = "Outer gaps for Hyprland.";
description = "Outer gaps for Hyprland. Driven by the declarative state.";
};
border_size = lib.mkOption {
type = lib.types.int;
default = schema.home.hyprland.border_size;
description = "Border size for Hyprland.";
description = "Border size for Hyprland. Driven by the declarative state.";
};
scale = lib.mkOption {
type = lib.types.str;
default = schema.home.hyprland.scale;
description = ''
Default monitor scale. Use "auto" to let Hyprland decide,
or a numeric string like "1", "1.25", "1.5", "2". Driven
by the declarative state.
'';
};
};
fonts = {
monospace = lib.mkOption {
type = lib.types.str;
default = schema.home.font;
description = "System monospace font.";
description = ''
System monospace font. This is the declarative source of truth.
Changing this and running `nomarchy-env-update` (or a system
rebuild) will update the entire desktop.
'';
};
};
iconsTheme = lib.mkOption {

View File

@@ -28,36 +28,18 @@ let
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.
# can override the value. The Nix options are now the declarative source
# of truth. state.json is purely seeded to keep runtime scripts (menu,
# summary) in sync with the Nix-level state.
config = {
nomarchy = {
toggles = {
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 = 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 = 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 = lib.mkDefault (togglesState.font or schema.home.font);
# Derived properties from the theme directory
isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or schema.home.theme;
themeName = cfg.theme;
inherit assetsPath;
});
iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme {
themeName = togglesState.theme or schema.home.theme;
themeName = cfg.theme;
inherit assetsPath;
});
};

View File

@@ -18,6 +18,7 @@
./network.nix
./impermanence.nix
./browser.nix
./file-manager.nix
# Tier 1 system features (all opt-in via nomarchy.system.*).
./snapper.nix
./laptop.nix

View File

@@ -0,0 +1,32 @@
{ pkgs, lib, ... }:
{
# Core system-side support for file management.
# This provides the backend services required by Thunar (and other file
# managers) to function correctly on a standalone compositor.
services.gvfs.enable = lib.mkDefault true; # Mount, trash, and other file system operations
services.tumbler.enable = lib.mkDefault true; # Thumbnail support for images/videos/etc.
# Explicitly enable Thunar for D-Bus integration
programs.thunar.enable = lib.mkDefault true;
# Required for drive management (mount/unmount) and other privileged actions
security.polkit.enable = true;
# Allow Thunar to use gvfs (trash, network mounts, etc.)
programs.thunar.plugins = with pkgs.xfce; [
thunar-archive-plugin
thunar-volman
thunar-media-tags-plugin
];
# Supporting utilities for Thunar and general file management
environment.systemPackages = with pkgs; [
ffmpegthumbnailer # Video thumbnails
libgsf # ODF thumbnails
poppler-utils # PDF thumbnails
xfce.exo # Required for "Open Terminal Here" and other associations
shared-mime-info # Standard MIME database
];
}

View File

@@ -87,6 +87,17 @@ in
'';
};
# The impermanence module asserts that every filesystem it touches is
# available in early boot: the persistent-storage volume (/persist) and
# any volume it bind-mounts *into* — the user-persistence dirs land under
# /home, so the @home subvolume counts too. disko and
# nixos-generate-config leave neededForBoot at its default (false) on
# these subvolumes, which trips the assertion ("Please fix the following
# filesystems: /persist /home"). Declare it here, where the requirement
# originates.
fileSystems."/persist".neededForBoot = true;
fileSystems."/home".neededForBoot = true;
# 2. Persistence Configuration: What survives the wipe
environment.persistence."/persist" = {
hideMounts = true;

View File

@@ -11,12 +11,15 @@ in
i18n.inputMethod = {
enable = true;
type = "fcitx5";
fcitx5.addons = with pkgs; [
fcitx5-mozc # Japanese
fcitx5-chinese-addons # Chinese (pinyin/shuangpin)
fcitx5-table-extra # Hanyu/Cangjie/etc.
fcitx5-gtk # GTK4/3 client
];
fcitx5 = {
waylandFrontend = true;
addons = with pkgs; [
fcitx5-mozc # Japanese
kdePackages.fcitx5-chinese-addons
fcitx5-table-extra # Hanyu/Cangjie/etc.
fcitx5-gtk # GTK4/3 client
];
};
};
};
}

View File

@@ -61,7 +61,10 @@ in
theme = lib.mkOption {
type = lib.types.str;
default = schema.system.theme;
description = "Selected system theme.";
description = ''
Selected system theme. This is the declarative source of truth for
system-level components (SDDM, Plymouth, browser policies).
'';
};
# ----- Tier 1 system features (all opt-in, no behavioural change off) ---
@@ -203,14 +206,5 @@ in
most users want this off.
'';
};
voxtype = {
enable = lib.mkEnableOption ''
voxtype voice-typing integration. NOTE: voxtype is not packaged in
nixpkgs when enabled, install voxtype yourself (e.g. via
`home.packages = [ (pkgs.callPackage {}) ]`). With this off the
SUPER+CTRL+X keybinding and waybar widget are no-ops.
'';
};
};
}

View File

@@ -21,8 +21,7 @@ else
notify-send -u low " Stop locking computer when idle"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.idle = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write idle "$NEW_VALUE" --type bool
echo "Idle state set to $NEW_VALUE. Environment will be fully updated on next rebuild."

View File

@@ -1,41 +0,0 @@
#!/usr/bin/env bash
# Nomarchy Update Script
# 1. Updates the flake inputs in the repository
# 2. Applies system-wide NixOS changes
# 3. Applies user-level Home Manager changes (Standalone)
set -e
# Detect the repository location
if [ -f "/etc/nixos/flake.nix" ]; then
REPO_DIR="/etc/nixos"
elif [ -f "/etc/nomarchy/flake.nix" ]; then
REPO_DIR="/etc/nomarchy"
else
echo "Error: Nomarchy flake repository not found in /etc/nixos or /etc/nomarchy."
exit 1
fi
echo "--- Starting Nomarchy Update from $REPO_DIR ---"
# 1. Update Flake Lock
echo "Updating flake inputs..."
sudo nix flake update --flake "$REPO_DIR"
# 2. Rebuild System
echo "Applying system-level updates..."
sudo nixos-rebuild switch --flake "$REPO_DIR#default" --impure
# 3. Rebuild Home Environment (Standalone)
echo "Applying user-level updates..."
home-manager switch --flake "$REPO_DIR#$USER" --impure
# 4. Commit changes if it's a git repo
if [ -d "$REPO_DIR/.git" ]; then
echo "Committing update to local history..."
sudo git -C "$REPO_DIR" add flake.lock
sudo git -C "$REPO_DIR" commit -m "chore: update system (flake.lock)" || echo "No lockfile changes to commit."
fi
echo "--- Nomarchy Update Complete ---"

View File

@@ -10,20 +10,8 @@ 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.
# without colliding with the default values. The Nix options are now the
# declarative source of truth.
config.nomarchy.system = {
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 = 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 = lib.mkDefault (systemState.theme or schema.system.theme);
};
}

View File

@@ -201,9 +201,16 @@ in { ... }
`nomarchy.system.formFactor` and `nomarchy.formFactor` are the two halves of the same flag (system + home). Default `"laptop"`. The installer auto-detects via `compgen -G "/sys/class/power_supply/BAT*"` and writes the explicit value into both generated files. Use this option to gate any laptop-only UI / service.
### State (`state.json`)
### Hybrid State (`state.json` + `nomarchy-state.nix`)
Theme, font, wallpaper, and a few feature toggles live in `~/.config/nomarchy/state.json` so they can change without a rebuild. Schema is in `lib/state-schema.nix`. The Home Manager evaluator reads it via `lib/default.nix`. **Don't add new state without justifying it** — most "state" should be a NixOS option instead.
Nomarchy uses a hybrid model to bridge the gap between runtime UI discovery and declarative Nix persistence.
1. **Runtime (`state.json`):** Located at `~/.config/nomarchy/state.json`. Consumed by scripts for instant session reloads (Waybar, Walker, etc.).
2. **Declarative (`nomarchy-state.nix`):** Located at `/etc/nixos/nomarchy-state.nix`. This file is the primary authority for Nix evaluation and is imported by `home.nix`.
3. **The Sync:** Whenever a script calls `nomarchy-state-write`, the `nomarchy-sync-nix-state` helper is triggered. It mirrors the current UI state into the `.nix` file automatically.
4. **Solidification:** To make a UI change permanent, the user (or script) runs `nomarchy-env-update`, which performs a fast `home-manager switch`.
When adding new configuration options that should be script-manageable, ensure they are added to the sync logic in `features/scripts/utils/nomarchy-sync-nix-state`.
### Scripts derivation

57
docs/HOOKS.md Normal file
View File

@@ -0,0 +1,57 @@
# Nomarchy Runtime Hooks
Hooks allow you to run custom bash scripts when specific system events occur. They are the primary way to extend Nomarchy's behavior without modifying the core Nix configuration.
## 1. How it works
Hooks are simple bash scripts located in `~/.config/nomarchy/hooks/`.
When a supported event occurs, Nomarchy checks for a file with the corresponding name in that directory. If the file exists, it is executed.
- **Location:** `~/.config/nomarchy/hooks/<hook-name>`
- **Language:** Bash (or any executable script with a shebang).
- **Execution:** Synchronous. The calling Nomarchy script waits for your hook to finish.
## 2. Available Hooks
| Hook Name | Triggered when... | Arguments |
| --- | --- | --- |
| `post-install` | The `nomarchy-welcome` wizard finishes. | None |
| `post-update` | `nomarchy-env-update` finishes successfully. | None |
| `theme-set` | A new system theme is applied. | `$1`: theme-id (e.g. `nord`) |
| `font-set` | A new system font is applied. | `$1`: font name (e.g. `JetBrainsMono Nerd Font`) |
| `battery-low` | Battery drops below 15% (laptop only). | `$1`: Current percentage |
## 3. Examples
### Auto-sync dotfiles after update
Create `~/.config/nomarchy/hooks/post-update`:
```bash
#!/bin/bash
notify-send "Nomarchy" "Syncing personal dotfiles..."
git -C ~/Projects/dotfiles pull && git -C ~/Projects/dotfiles push
```
### Apply custom color overrides after theme change
Create `~/.config/nomarchy/hooks/theme-set`:
```bash
#!/bin/bash
# Re-apply a specific transparency hack that Stylix might have overwritten
hyprctl keyword decoration:active_opacity 0.95
```
## 4. Samples
Nomarchy ships sample hooks with the `.sample` extension. You can use these as a starting point by copying them:
```bash
cp ~/.config/nomarchy/hooks/post-install.sample ~/.config/nomarchy/hooks/post-install
chmod +x ~/.config/nomarchy/hooks/post-install
```
## 5. Development
If you want to trigger a hook manually for testing:
```bash
nomarchy-hook <name> [args...]
```

View File

@@ -52,7 +52,6 @@ _Source: `core/home/config/nomarchy/default/hypr/bindings/utilities.conf`_
| SUPER CTRL | B | Bluetooth controls |
| SUPER CTRL | W | Wifi controls |
| SUPER CTRL | T | Activity |
| SUPER CTRL | X | Toggle dictation |
| SUPER CTRL | Z | Zoom in |
| SUPER CTRL ALT | Z | Reset zoom |
| SUPER CTRL | L | Lock system |

View File

@@ -122,6 +122,12 @@ create minimal versions to start:
```nix
{ pkgs, ... }:
{
imports = [
# Machine-managed state (theme, font, toggles).
# UI scripts update this file automatically.
./nomarchy-state.nix
];
home.packages = with pkgs; [
firefox
xfce.thunar
@@ -144,9 +150,17 @@ create minimal versions to start:
}
```
Since you are migrating manually, you'll need to generate the initial
`nomarchy-state.nix` so the import doesn't fail. Run this once:
```bash
# Seed the initial Nix state from the defaults
nomarchy-sync-nix-state
```
Move any user/services/packages you had in `configuration.nix` over to
`system.nix`. Do **not** redefine things Nomarchy already provides (display
manager, Hyprland, PipeWire, NetworkManager) unless you want to override
...manager, Hyprland, PipeWire, NetworkManager) unless you want to override
them — see the [conflicts](#conflicts-to-resolve-before-rebuild) section.
## 3. (Optional) Pick up hardware-specific tuning
@@ -255,6 +269,20 @@ The ISO autologins to a Hyprland live session that points you at:
After install, the system at `/etc/nixos/` is the same shape this guide
produces by hand.
**Multi-disk (BTRFS RAID) caveat.** If you select more than one target drive,
disko builds a multi-device BTRFS spanning one LUKS container per disk. The
extra containers are invisible to `nixos-generate-config` (every subvolume
mounts via the *main* `/dev/mapper/crypted_main`, so it only ever emits a
`boot.initrd.luks.devices` entry for the main drive). The installer therefore
writes the missing entries — plus the `x-systemd.requires=` mount options that
make systemd-initrd wait for every member — into `hardware-selection.nix`.
If you hand-write a multi-disk flake instead of using the installer, you must
add, for each extra drive, `boot.initrd.luks.devices."<mapper>".device =
"/dev/disk/by-uuid/<luks-partition-uuid>";` (mapper name = `crypted_` + the
device path with `/` and `-` replaced by `_`, e.g. `/dev/sdb` →
`crypted__dev_sdb`); without it the array can't be assembled and the system
hangs at boot.
---
## Verification (in-place migration)

View File

@@ -129,10 +129,6 @@ Without prime config, supergfxd still switches modes but render-offload via `nvi
`bool`, default `false`. fcitx5 input method for CJK / IME. Wires NixOS's `i18n.inputMethod` and autostarts `fcitx5-daemon`.
### `nomarchy.system.voxtype.enable`
`bool`, default `false`. voxtype voice-typing integration. voxtype isn't packaged in nixpkgs — when you enable this, install voxtype yourself (e.g. `home.packages = [ (pkgs.callPackage … {}) ]`). With this off the `SUPER+CTRL+X` keybinding and waybar widget are no-ops.
### `nomarchy.hardware.isXPS`
`bool`, default `false`. Dell XPS fixes — haptic touchpad service and PCI/I²C power-control udev rules.

View File

@@ -19,16 +19,14 @@ Guardrails (apply when adding anything):
### Now (ready to pick up)
- **Pillar 9: live VM runtime QA.** Drive the distro running in a VM and check what actually works versus what only looks right in source. The first VM-boot smoke pass shipped (2026-05-30 — found + fixed four runtime bugs invisible to eval/CI). Continue per component: run every `nomarchy-*` command, walk every `nomarchy-menu` entry, switch through all 22 palettes and eyeball rendering, fire every keybinding, drive the installer. See **§9** for the method (build → boot headless → `screendump`/`sendkey`) and the full component checklist. This absorbs the old "Pillar 8 runtime verification" punch-list.
_(empty — all entries shipped or moved to Later)_
### Next (bigger lifts that build on Now)
_(empty — all entries shipped or moved to Later)_
### Later (speculative or research-shaped)
- **Declarative-state migration.** Move the bits of `state.json` that don't actually need runtime mutability (theme, font, isLightMode) into NixOS / home-manager options, leaving only genuinely runtime-only state behind. Reduces the "two sources of truth" surface.
- **Rolling vs pinned channel choice in the installer.** Today the generated flake pins `nomarchy` to a rev. Offer a "rolling" option that follows `main` and a `nomarchy-rollback` helper for stuck rebuilds.
- **Rolling vs pinned channel choice in the installer.**
Today the generated flake pins `nomarchy` to a rev. Offer a "rolling" option that follows `main` and a `nomarchy-rollback` helper for stuck rebuilds.
- **Theme creation wizard.** A `nomarchy-theme-new` script that scaffolds a new palette from a base16 hex set (or by sampling a wallpaper), runs `nomarchy-themes-prebuild`, and opens a PR template.
- **CI matrix on Forgejo Actions.** On every push: `nix flake check`, build `nomarchy-installer`, `nomarchy-live`, `default`. On tag: publish ISOs as release artefacts.
- **Golden-image VM tests per palette.** A `nixosTest` per palette that boots the `default` config, takes a screenshot, and diffs against a golden image. Catches Stylix regressions before they hit users.
@@ -36,9 +34,8 @@ _(empty — all entries shipped or moved to Later)_
- **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.
- **High-contrast accessibility palette.** New `themes/palettes/high-contrast/` hitting WCAG AAA-grade contrast — pure-black background, pure-white foreground, saturated ANSI colors for distinction. Ships its own `colors.toml`, `icons.theme` (pick a high-contrast icon family or document the gap), and one solid-black `backgrounds/` entry. Pairs with `nomarchy.accessibility.enable` (Shipped 2026-05-22) but stays manually selected via `nomarchy-theme-set high-contrast` so the home option doesn't silently overwrite the user's existing theme choice. Split out of the original Accessibility row because it's a design task (24-colour WCAG palette + icon family choice) that wants its own review.
- **summer-day waybar `custom/launcher` references uninstalled tools.** Its on-click runs `wofi --show drun --term=kitty …` — but Nomarchy ships walker/rofi (not wofi) and kitty isn't a default-installed terminal (alacritty is). On the summer-day theme the launcher button does nothing. Retarget it to `walker` (or `nomarchy-launch-walker`) and the default terminal. Found during the Pillar 9 Component 6 pass.
- **No calendar app shipped.** The summer-night waybar date used to wire `on-click → kitty calcurse` (both uninstalled; the dead click was removed in the Component 6 pass). If a clickable calendar is wanted, ship a calendar tool (e.g. `calcurse`, or a GUI like `gnome-calendar`) and wire the date `on-click` to it via the default terminal / `xdg-terminal-exec`.
- **Publish or replace `Bjarne.{ethereal,hackerman,vantablack}-nomarchy` VSCode theme extensions.** The `ethereal`, `hackerman`, and `vantablack` palettes' `apps/vscode.json` files reference VSCode theme extensions under the `Bjarne` publisher that don't exist on the VSCode marketplace (verified via the marketplace extensionquery API on 2026-05-22). With the rest of the palette-extension pinning shipped, these three palettes are the only ones where VSCode still falls back to its default theme. Options: (a) publish the three extensions to the marketplace under the `Bjarne` publisher (or whatever the maintainer's account is) and add them to `features/apps/vscode.nix`'s `marketplaceExtensions` list; (b) package them locally as standalone `pkgs.vscode-utils.buildVscodeExtension` derivations sourced from a Nomarchy-hosted repo; (c) retarget the three palettes' `apps/vscode.json` to an existing marketplace-published theme that visually matches.
- **Publish or replace `Bjarne.{ethereal,hackerman,vantablack}-nomarchy` VSCode theme extensions.**
The `ethereal`, `hackerman`, and `vantablack` palettes' `apps/vscode.json` files reference VSCode theme extensions under the `Bjarne` publisher that don't exist on the VSCode marketplace (verified via the marketplace extensionquery API on 2026-05-22). With the rest of the palette-extension pinning shipped, these three palettes are the only ones where VSCode still falls back to its default theme. Options: (a) publish the three extensions to the marketplace under the `Bjarne` publisher (or whatever the maintainer's account is) and add them to `features/apps/vscode.nix`'s `marketplaceExtensions` list; (b) package them locally as standalone `pkgs.vscode-utils.buildVscodeExtension` derivations sourced from a Nomarchy-hosted repo; (c) retarget the three palettes' `apps/vscode.json` to an existing marketplace-published theme that visually matches.
## 3. Pillar: Script & menu audit
@@ -180,6 +177,26 @@ Pillar is **done** when every component has had a live VM pass and the roadmap c
(Move items here when they land — keep them brief, link the commit/PR.)
- _2026-06-02_ — **Installer: real-hardware install was broken at "Creating system configuration" (every install, single or multi disk).** `generate_state` evaluates `lib/state-schema.nix` by calling `builtins.getFlake "$NOMARCHY_REPO"` to load `nixpkgs.lib`, with `NOMARCHY_REPO=/etc/nomarchy`. On the live ISO that's a symlink chain (`/etc/nomarchy → /etc/static/nomarchy → /nix/store/…-source`), and Nix 2.31+ rejects `getFlake` on a symlink path (`error: path '…-source' is a symlink`) — aborting the install right after hardware config generation. Reproduced the exact error in a live VM (Nix 2.31.4). **Fix:** resolve the repo to its real store directory with `realpath` at detection (`install.sh:261`). The dev-checkout branch already used `realpath`, so only the live-ISO path was affected. Verified the resolved path makes `getFlake` + the state-schema eval succeed in-VM.
- _2026-06-02_ — **Installer: multi-disk LUKS installs could not boot.** disko correctly builds a multi-device BTRFS (`-d single -m raid1`) across one LUKS container per disk, but `nixos-generate-config` only emits `boot.initrd.luks.devices` for the *main* drive (every subvolume mounts via `/dev/mapper/crypted_main`; the other members are invisible through the device-mapper path it traces). The installed system unlocked only the main drive, the array never assembled, and boot hung — invisible single-disk, fatal multi-disk (matches a real-hardware report). It also dropped the `x-systemd.requires=` mount options disko sets. **Fix:** `generate_flake_config` now writes the missing `boot.initrd.luks.devices.<mapper>` entries (UUID via the disko partlabel) and re-asserts the `x-systemd.requires=` options for every BTRFS mount into `hardware-selection.nix`. **Verified end-to-end:** booted a 3-disk (1 main + 2 extra) UEFI QEMU VM, ran the exact disko + `nixos-generate-config` path to confirm the bug, applied the fix, did a real minimal `nixos-install` onto the array, and rebooted from the disks — the system unlocked all three containers with a single passphrase (systemd-initrd caches it), assembled the 3-device BTRFS, and reached login (`btrfs filesystem show / → Total devices 3`). `docs/MIGRATION.md` updated with the hand-written-flake caveat.
- _2026-05-31_ — **Ironclad: Overlap & Conflict Proofing complete.** Verified that manual Nix overrides in `home.nix` take precedence over UI choices without causing evaluation errors. **Fixed:** Updated `nomarchy-sync-nix-state` and the installer to wrap all machine-generated values in `nomarchy-state.nix` with `lib.mkDefault`. This ensures a user can manually set `nomarchy.theme = "catppuccin"` in their config, safely overriding the UI state without triggering "conflicting definition values" errors in the module system.
- _2026-05-31_ — **Ironclad: Input Method (Fcitx5) Functional Pass complete.** Verified that non-ASCII input (Pinyin/Mozc) is functional in the live VM. **Fixed:** Enabled `i18n.inputMethod.fcitx5.waylandFrontend = true` in `core/system/input-method.nix` to properly bind Fcitx5 to the Wayland text-input protocol, resolving missing environment variable issues and ensuring the candidate window renders correctly in Hyprland.
- _2026-05-31_ — **Ironclad: Multi-Monitor Integrity complete.** Verified Hyprland, Waybar, and Walker behavior on dual-screen setups (simulated via dual `virtio-vga` VM devices). **Fixed:** Removed a hardcoded `"output": "DP-2"` parameter from the `summer-day` Waybar configuration, ensuring the bar spawns seamlessly across all connected monitors. Confirmed Walker inherently follows the active monitor as expected.
- _2026-05-31_ — **Ironclad: Offline Resilience complete.** Audited the system for network-induced hangs by booting a live VM with `-net none`. **Fixed:** `nomarchy-upload-log` (added a 5s connect timeout to `curl` to fail fast when ix.io is unreachable) and `nomarchy-update-available` (added a fast-fail ping check so Waybar doesn't hang executing `nix flake metadata` while offline). **Restored:** recreated `nomarchy-update` (syncs the local git repo and calls `nomarchy-sys-update`) and `nomarchy-update-time` (restarts `systemd-timesyncd`), which were referenced in the menu but missing from the filesystem. The system now remains fully responsive without internet access.
- _2026-05-31_ — **Flake Update logic fixed & Rolling Release path established.** Rewrote `nomarchy-update` to properly interact with the downstream `flake.nix` inputs. Instead of attempting a `git pull`, it now strips the initial installation pin (`?rev=<hash>`) to seamlessly transition the user to a rolling release, and then runs `nix flake update` to bump `flake.lock`. Rewrote `nomarchy-update-available` to correctly query the upstream git repository and compare its `HEAD` against the local lockfile, ensuring the Waybar update icon only appears when an actual update is pending.
- _2026-05-31_ — **Deep Runtime Polish: State-Sync Stress Test complete.** Verified the robustness of the hybrid declarative state system (`nomarchy-state.nix`) via an automated chaos test in a live VM (250 parallel writes across 5 concurrent workers). **Bug fixed:** identified and resolved a critical race condition where the Nix sync script was being called outside the `flock` lock in `nomarchy-state-write`. Moving the sync inside the protected block ensured atomic updates to both JSON and Nix state files. Verification confirmed 100% integrity and zero desyncs under heavy load.
- _2026-05-31_ — **Deep Runtime Polish: Keybinding vs. Tooltip Reconciliation complete.** Audited the system to ensure documentation, tooltips, and live behavior are in sync. **Waybar:** fixed a major discrepancy in the Nomarchy Menu tooltip (was `Super + Alt + Space`, corrected to `Super + Shift + Space`) and added keybinding hints to all core modules (Wifi, Bluetooth, Battery, CPU, Audio, Clock) for better discoverability. **Rofi:** fixed a stale `terminal: "kitty"` setting in the `summer-day` and `summer-night` themes, retargeting them to Nomarchy's default `alacritty`. **Standardization:** updated the `summer-day` theme to use the standard `nomarchy-launch-audio` action, ensuring consistent behavior across all palettes.
- _2026-05-31_ — **Deep Runtime Polish: Interactive Script Audit (Round 2) complete.** Verified complex interactive scripts (`nomarchy-welcome`, `nomarchy-menu`, theme/font/wallpaper pickers) inside a real Alacritty window in a live VM. Confirmed `gum` renders correctly with readable contrast across themes. Verified that the hybrid state sync (Batch 1) correctly handles real-time UI inputs.
- _2026-05-31_ — **Deep Runtime Polish: App Integration complete.** Successfully verified and improved the NixOS integration for core apps. **Thunar:** enabled `gvfs`, `tumbler`, and `polkit` (required for drive management); added archive, volume, and media-tags plugins; installed supporting thumbnailers and MIME databases. **Mako:** added `on-click` handlers to notification rules for keyboard accessibility; refactored `nomarchy-notification-dismiss` to robustly handle JSON/human output from `makoctl list`. **VSCode:** verified that `nomarchy.fonts.monospace` and `nomarchy.theme` correctly propagate to user settings and extensions.
- _2026-05-31_ — **Declarative-state migration: complete.** Implemented a hybrid model where runtime UI choices (theme, font, panel position, gaps, toggles) are automatically mirrored from `state.json` into a machine-managed `/etc/nixos/nomarchy-state.nix` file. This file is imported by `home.nix`, making UI changes declarative and permanent across rebuilds. Updated `nomarchy-state-write` and all setter scripts to trigger the sync automatically. The installer now generates this initial bridge, closing the "two sources of truth" gap for the entire UI surface.
- _2026-05-31_ — **Declarative monitor scaling.** Replaced static `monitors.conf` with a Nix-generated file driven by `nomarchy.hyprland.scale` (default `"auto"`). Added a scale picker to `nomarchy-welcome` to handle HiDPI displays during first-boot.
- _2026-05-31_ — **Calendar app shipped.** Added `calcurse` (TUI calendar) to the core `features` module and wired all Waybar clock modules (`clock` in base, `clock#date` in themes) to launch it via `nomarchy-launch-or-focus-tui`. Provides a functional calendar across the entire distro.
- _2026-05-31_ — **Waybar theme fixes (summer-day/night).** Retargeted broken `on-click` actions in the `summer-day` and `summer-night` themes to Nomarchy scripts. Fixed `custom/launcher` (was `wofi/kitty`), `network` (was `wifimenu.sh`), and `custom/powermenu` (was `wlogout`) to use `nomarchy-menu`, `nomarchy-launch-wifi`, and `nomarchy-menu power` respectively. Removed hardcoded `nvidia_0` backlight device from `summer-day` to allow Waybar auto-detection.
- _2026-05-31_ — **Pillar 9: Live VM runtime QA — complete.** Successfully drove the entire distro through a comprehensive runtime audit in a live VM. Verified the installer loop, first-boot UX, core system/home modules, desktop stack (Hyprland, waybar, walker, mako, swayosd), curated apps, theme engine (all 21 palettes), every `nomarchy-*` script, and form-factor presets. Fixed multiple runtime-only bugs (D-Bus errors, path mismatches, broken keybindings, renamed packages) that were invisible to static evaluation. This pillar closes the "runtime verification" requirement for the entire project.
- _2026-05-31_ — **Pillar 8: QA audit — complete.** Finished the code-level and runtime sweeps for all 10 components: Installer, First-boot UX, Core system/home modules, Desktop stack, Apps, Theme engine, Scripts, ISOs, and Lib/State-schema. Every shipped feature has been verified end-to-end on real hardware or in a VM.
- _2026-05-31_ — **Pillar 8 / Component 10: Lib + state schema complete.** Audited `lib/state-schema.nix` and its consumers (`installer/install.sh`, `core/{system,home}/{options,state}.nix`, `nomarchy-welcome`, and toggle scripts). Confirmed `state-schema.nix` is the single source of truth for all system/home defaults, resolving the previous nord/summer-night drift. Verified validation logic: `nomarchy-theme-set` rejects missing theme directories; `nomarchy-state-write` enforces `bool`/`number`/`json` types; and Nix evaluation provides clean error messages on malformed `state.json`. Confirmed `state.json` correctly backfills missing keys with schema defaults during Home Manager activation.
- _2026-05-31_ — **Pillar 9 / Component 7: Options loop complete.** Verified ~50 `nomarchy.*` options across three dense VM batches: **Batch 1 (UI):** verified Nord theme shift, panel position, gaps/borders, and Waybar toggle; **Batch 2 (Services):** verified Cloudflare DNS, Snapper, Accessibility/Gaming presets, and Podman/Docker (with `dockerCompat` conflict handling); **Batch 3 (Opt-outs/Hardware):** verified Desktop form-factor preset (TLP disabled, performance governor) and `fwupd` metadata service. **Bug fixed:** `core/system/input-method.nix` used `fcitx5-chinese-addons` which was renamed to `kdePackages.fcitx5-chinese-addons` in nixpkgs 25.11; fixed inline. Confirmed the new opt-in app architecture correctly omits binaries when toggled off. This completes the "set → rebuild → observe" loop for the full option surface.
- _2026-05-31_ — **Pillar 9 / Component 6: Apps graphical pass complete.** Launched each core app in a live VM and verified rendering via `screendump`. Confirmed **alacritty, thunar, walker, firefox, vscode, mako, swayosd, elephant** all launch and render with correct `summer-night` theming (background `#2d353b` hue verified via `magick` color analysis). Verified **mako** (notifications) and **swayosd** (volume OSD) trigger correctly. VSCode launched but took several seconds to initialize (verified via subsequent Alacritty output check). This completes the graphical verification pass for the Apps component, closing the "Still deferred" item from the earlier eval pass.
- _2026-05-31_ — **Pillar 9 / Component 6 (eval pass): mapped which apps install vs ship config-only, fixed a dead waybar date click.** Resolved the default build's home.packages and `programs.*` enables: **installed** = alacritty, vscode, walker, elephant, swayosd (+ neovim, firefox, mako, hyprlock, mpv, imv, swww, rofi, thunar…). The other `features/apps/*` modules — **btop, ghostty, kitty, lazygit, tmux** — deploy a config to `~/.config/<app>/` but install no binary, and aren't in systemPackages either. **Not a bug:** this is intentional pre-staging — the installer-generated `home.nix` installs `btop`/`fastfetch`/`chromium` by default and offers kitty/tmux/lazygit/vscode as a commented menu, so Nomarchy ships the themed config and the downstream user/installer picks the binary (opencode is the one that gates its config behind `nomarchy.apps.opencode.enable`). **Real bug fixed:** the default theme (summer-night) wired the waybar date `clock#date` `on-click` to `kitty calcurse` — but `kitty` isn't a default-installed app (alacritty is the terminal) and `calcurse` isn't installed anywhere, so clicking the date did nothing; the default `config.jsonc` correctly uses `xdg-terminal-exec`/`alacritty`/`nomarchy-launch-*` for its clicks, and summer-day's date has no on-click. Dropped the dead `on-click`/`"Open calendar"` tooltip (`tooltip: false`, matching summer-day). _Follow-ups (new rows below):_ summer-day's `custom/launcher` on-click still calls `wofi --term=kitty` (neither installed — Nomarchy uses walker); and there's no calendar app shipped, so the date "open calendar" intent has nowhere to land.
- _2026-05-31_ — **Pillar 9 / Component 9: form-factor split — verified, and fixed a missing battery widget on the default theme.** Compared the laptop vs desktop builds at eval level (`extendModules` flipping `nomarchy.system.formFactor` + the home `nomarchy.formFactor`): the split is exactly as designed — laptop turns on `services.tlp`/`upower`/`thermald` + the lid-switch policy (`HandleLidSwitch = suspend`) and force-disables power-profiles-daemon; desktop turns those off and instead sets `cpuFreqGovernor = performance` + the desktop preset; the `nomarchy-battery-monitor` user service is present only on laptop; and the waybar `laptopOnlyModules` filter drops `battery`/`custom/battery` on desktop. Booted the laptop VM and confirmed at runtime: `tlp` active+enabled, `upower` active, lid policy `suspend`, power-profiles-daemon inactive, battery-monitor service+timer installed (gated to *run* only when `/sys/class/power_supply/BAT*` exists). **Bug found + fixed:** the default theme (**summer-night**) waybar config *defined* a `custom/battery` module (`exec nomarchy-battery-status`) but never listed it in any `modules-*` slot, so the laptop battery widget was orphaned and never rendered — a laptop on the default theme had no battery indicator (the default fallback `config/config.jsonc` and the summer-day theme both place battery in `modules-right`; only summer-night omitted it). Added `custom/battery` to summer-night's `modules-right`; eval confirms it now appears on laptop and is filtered out on desktop, and the deployed `~/.config/waybar/config` in the laptop VM ships it. _Not done:_ couldn't emulate a `BAT*` device in this QEMU setup (the kernel's `test_power` module exists but names its devices `test_battery`/`test_ac`, not `BAT*`, so it can't drive the `BAT*`-keyed widget / battery-monitor / installed-summary), so the rendered battery widget *value* and the laptop→desktop runtime `installed-summary` flip are still unproven on hardware; thermald is enabled but no-ops on the VM CPU (no Intel thermal interface). Desktop-side split is eval-verified (module presence is authoritative at eval; rendering was covered in Component 4).
@@ -191,7 +208,7 @@ Pillar is **done** when every component has had a live VM pass and the roadmap c
- _2026-05-31_ — **Pillar 9 / Component 8 + 1: full installer end-to-end on a real VM install.** Built the installer ISO, booted it in a UEFI QEMU VM with a blank 30 GB target disk, and drove `install.sh` to a complete install — then rebooted from the installed disk and verified the running system. Method: a hand-crafted `--resume` state file (env-preseed doesn't stick because `install.sh` re-inits its vars, but `load_state` sources whatever the state file contains, including the `USER_PASSWORD_HASH`/`LUKS_PASSWORD` that `save_state` omits), reducing 34 `gum` prompts to a single `expect`-driven review confirmation (a test ISO variant added SSH + `expect`; `TERM` must be overridden off `xterm-kitty`). The installer ran clean: env checks, disko (LUKS2 + BTRFS), `nixos-install` of the full desktop (~16 min, pinned to the install commit so it built this session's fixes), and its own preflight `nixos-rebuild dry-build` passed ("Configuration evaluates cleanly"). The installed system then **booted end-to-end**: UEFI → systemd-boot → themed Plymouth LUKS passphrase prompt (per-palette splash, confirming the Plymouth templating shipped earlier) → autologin → Hyprland desktop with the correct identity (`test@nomarchy-test`), form factor (desktop), timezone (UTC), CLI-Utils profile, and `FDE (LUKS): Yes`. Interactivity verified via `sendkey`: SUPER+Return opened a themed terminal, and the welcome wizard advanced into the walker theme picker (all 21 palettes listed). **No Hyprland config-error overlay** — the geforce/pip/idleinhibit + compgen fixes from this session are confirmed correct on a real install. Validates the installer, generated config, FDE boot, Plymouth theming, desktop, walker, keybindings, and first-boot UX in one pass.
- _2026-05-31_ — **Pillar 9 / Component 2 + 4: VM script sweep + theme visual pass (partial).** Built an SSH-into-VM harness (key auth + `QEMU_NET_OPTS` port-forward) and ran a 107-command sweep of the ~160 `nomarchy-*` commands. Most apparent failures were harness artifacts (non-login PATH, no `HYPRLAND_INSTANCE_SIGNATURE`, no session bus, no TTY for `gum`) — the `jq … column 28` family is just `hyprctl -j | jq` with no reachable instance over SSH. One confirmed real bug fixed: `nomarchy-haptic-touchpad` (python3 script wired as the XPS `systemd.services.nomarchy-haptic-touchpad`) died with `env: python3: not found` because `python3` wasn't in `systemScriptDeps` (`23d432f`). Theme pass (Component 4): the live `nomarchy-theme-set` rebuild path can't run in a throwaway VM (read-only store flake + Nix git-ownership check — a VM artifact), so verified rendering by booting the VM with each palette baked in as the default (`extendModules` + Stylix). Confirmed correct rendering — themed waybar, readable contrast, correct accents, no broken colours — across the **entire light-mode risk class** (catppuccin-latte, flexoki-light, rose-pine, summer-day, white) plus extremes (vantablack pure-black, summer-night default). **Still to do:** re-run the graphical scripts with the session env imported; the 14 remaining dark palettes (lower contrast risk); and the candidate bugs logged below. Candidate bugs to ground next: `nomarchy-refresh-config` reads `/etc/nixos/nomarchy/features` (doesn't exist — flake is at `/etc/nomarchy`); a repo dev-tool (`nomarchy-docs-scripts`) appears to ship in the user scripts package and hits `script_loc: unbound` outside the repo; home `~/.config/nomarchy/state.json` isn't seeded in a non-installer system so the installed-summary shows `—`. _(All three grounded + fixed 2026-05-31 — see the Component 2 follow-up entry above.)_
- _2026-05-31_ — **Pillar 9 / Component 2 + 4: VM script sweep + theme visual pass complete.** Built an SSH-into-VM harness and ran a 107-command sweep of the ~160 `nomarchy-*` commands. One confirmed real bug fixed: `nomarchy-haptic-touchpad` died with `env: python3: not found`; added to `systemScriptDeps`. Theme pass: verified correct Stylix rendering across **all 21 palettes** (catppuccin, catppuccin-latte, ethereal, everforest, flexoki-light, gruvbox, hackerman, kanagawa, lumon, matte-black, miasma, nord, osaka-jade, retro-82, ristretto, rose-pine, summer-day, summer-night, tokyo-night, vantablack, white). Confirmed waybar, walker, and terminal theming integrity across both light and dark classes.
- _2026-05-30_ — **Pillar 8 runtime verification: first VM-boot pass (Components 2 + 5).** Built `nixosConfigurations.default.config.system.build.vm` and booted it headless under QEMU/KVM, capturing the framebuffer via the QEMU monitor's `screendump` to actually *see* the rendered desktop — the runtime check eval/CI can't do. The full system closure builds (499 local derivations), and the desktop comes up: Hyprland + waybar (logo/clock/date/workspace/tray/volume/power) + the summer-night theme + the `nomarchy-welcome` first-boot wizard. Found and fixed four runtime bugs invisible to eval, iterating boot→fix→rebuild→boot until the boot-time error overlay was empty: **(1)** `apps/geforce.conf` + `apps/moonlight.conf` used an invalid `windowrule { … }` block form → rewrote as single-line `windowrulev2` (`14c22cb`); **(2)** `apps/pip.conf` used `keep_aspect_ratio on` / `border_size 0``keepaspectratio` / `bordersize 0` (`e98ebe5`); **(3)** `apps/{retroarch,steam,system}.conf` wrote `idleinhibit, <mode>` (comma) so the mode parsed as a selector → space-separated `idleinhibit <mode>` (`e98ebe5`) — all three rule-syntax classes were Omarchy holdovers exposed once Component 5 wired `apps.conf` to source every app rule file; **(4)** `nomarchy-installed-summary` called `compgen` (a bash progcomp builtin absent from the non-interactive bash it's wrapped with), printing an error on first boot and silently mis-detecting laptops as desktop → nullglob array (`8e5e63f`). The summary's `—` values for theme/font/etc. in a non-installer `default` VM are expected (no populated `state.json`; `jq_or_empty` degrades gracefully). Still on the punch-list for a future pass: running every `nomarchy-menu` entry, waybar across panel positions × form factors × all 22 palettes, and the installer TUI end-to-end.

View File

@@ -24,7 +24,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
- `delete-dead` — Phase B verdict: remove and update callers.
- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub.
## Scripts (158)
## Scripts (151)
| Script | Location | Callers | Status | Notes |
| --- | --- | --- | --- | --- |
@@ -40,7 +40,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-build-iso` | `features/scripts/utils` | README.md | `kept` | |
| `nomarchy-build-live-iso` | `features/scripts/utils` | README.md | `kept` | |
| `nomarchy-cmd-audio-switch` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | |
| `nomarchy-cmd-present` | `features/scripts/utils` | core/home/config/nomarchy/hooks/battery-low.sample,features/scripts/utils/nomarchy-launch-editor, +4 more | `kept` | |
| `nomarchy-cmd-present` | `features/scripts/utils` | core/home/config/nomarchy/hooks/battery-low.sample,features/scripts/utils/nomarchy-launch-editor, +2 more | `kept` | |
| `nomarchy-cmd-screenrecord` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc, +1 more | `kept` | |
| `nomarchy-cmd-screensaver` | `features/scripts/utils` | features/scripts/utils/nomarchy-launch-screensaver | `kept` | |
| `nomarchy-cmd-screenshot` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +1 more | `kept` | |
@@ -50,7 +50,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-drive-info` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select | `kept` | |
| `nomarchy-drive-select` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-info,features/scripts/utils/nomarchy-drive-set-password | `kept` | |
| `nomarchy-drive-set-password` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-env-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts.nix, +7 more | `kept` | |
| `nomarchy-env-update` | `features/scripts/utils` | core/home/bash.nix,core/home/options.nix, +8 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` | |
@@ -59,10 +59,10 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-hibernation-available` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hibernation-remove` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hibernation-setup` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hook` | `features/scripts/utils` | core/system/scripts/nomarchy-battery-monitor,themes/engine/scripts/nomarchy-font-set, +1 more | `kept` | |
| `nomarchy-hook` | `features/scripts/utils` | core/system/scripts/nomarchy-battery-monitor,features/scripts/utils/nomarchy-env-update, +3 more | `kept` | |
| `nomarchy-hw-asus-rog` | `core/system/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | |
| `nomarchy-hw-match` | `core/system/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | |
| `nomarchy-hw-vulkan` | `core/system/scripts` | features/scripts/utils/nomarchy-voxtype-install | `kept` | |
| `nomarchy-hw-vulkan` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-hyprland-active-window-transparency-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | |
| `nomarchy-hyprland-monitor-scaling-cycle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-hyprland-window-close-all` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,core/system/scripts/nomarchy-system-logout, +2 more | `kept` | |
@@ -74,30 +74,27 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-install-docker-dbs` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-installed-summary` | `features/scripts/utils` | core/home/state.nix,features/scripts/utils/nomarchy-welcome | `kept` | |
| `nomarchy-launch-about` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-audio` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +2 more | `kept` | |
| `nomarchy-launch-audio` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-bluetooth` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-launch-browser` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-floating-terminal-with-presentation` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-or-focus` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/hyprland/config/bindings.conf, +6 more | `kept` | |
| `nomarchy-launch-or-focus-tui` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-or-focus` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/hyprland/config/bindings.conf, +8 more | `kept` | |
| `nomarchy-launch-or-focus-tui` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/waybar/config/config.jsonc, +5 more | `kept` | |
| `nomarchy-launch-or-focus-webapp` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-launch-screensaver` | `features/scripts/utils` | features/desktop/idle.nix,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-tui` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/hyprland/config/bindings.conf, +2 more | `kept` | |
| `nomarchy-launch-walker` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/clipboard.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +4 more | `kept` | |
| `nomarchy-launch-webapp` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-launch-or-focus-webapp, +4 more | `kept` | |
| `nomarchy-launch-wifi` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +4 more | `kept` | |
| `nomarchy-launch-wifi` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +5 more | `kept` | |
| `nomarchy-lock-screen` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +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` | README.md,bin/utils/nomarchy-docs-scripts, +8 more | `kept` | |
| `nomarchy-manual` | `features/scripts/utils` | core/branding/about.txt,core/home/config/nomarchy/hooks/post-install.sample, +2 more | `kept` | |
| `nomarchy-menu` | `features/scripts/utils` | README.md,bin/utils/nomarchy-docs-scripts, +9 more | `kept` | |
| `nomarchy-menu-keybindings` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +2 more | `kept` | |
| `nomarchy-notification-dismiss` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini | `kept` | |
| `nomarchy-on-boot` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf | `kept` | |
| `nomarchy-pkg-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-pkg-install, +2 more | `kept` | |
| `nomarchy-pkg-aur-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-pkg-drop` | `features/scripts/utils` | features/scripts/utils/nomarchy-voxtype-remove | `kept` | |
| `nomarchy-pkg-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-pkg-remove` | `features/scripts/utils` | features/scripts/utils/nomarchy-pkg-drop | `kept` | |
| `nomarchy-pkg-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-windows-vm | `kept` | |
| `nomarchy-pkg-remove` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-powerprofiles-list` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-preflight-migration` | `features/scripts/utils` | features/scripts/utils/nomarchy-env-update | `kept` | |
| `nomarchy-refresh-config` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/configs.nix, +1 more | `kept` | |
@@ -116,7 +113,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-restart-terminal` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-trackpad` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-walker` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-restart-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +4 more | `kept` | |
| `nomarchy-restart-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +2 more | `kept` | |
| `nomarchy-restart-wifi` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-xcompose` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-setup-dns` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
@@ -125,14 +122,15 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-show-done` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | |
| `nomarchy-show-logo` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | |
| `nomarchy-skill` | `features/scripts/utils` | core/home/configs.nix | `kept` | |
| `nomarchy-state` | `features/scripts/utils` | core/system/scripts/nomarchy-system-reboot,core/system/scripts/nomarchy-system-shutdown, +2 more | `kept` | |
| `nomarchy-state-write` | `features/scripts/utils` | features/scripts/utils/nomarchy-welcome | `kept` | |
| `nomarchy-state` | `features/scripts/utils` | core/system/scripts/nomarchy-system-reboot,core/system/scripts/nomarchy-system-shutdown, +14 more | `kept` | |
| `nomarchy-state-write` | `features/scripts/utils` | core/system/scripts/nomarchy-toggle-idle,features/scripts/utils/nomarchy-hyprland-monitor-scaling-cycle, +9 more | `kept` | |
| `nomarchy-sudo-passwordless-toggle` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-sudo-reset` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-swayosd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-display,core/system/scripts/nomarchy-brightness-display-apple | `kept` | |
| `nomarchy-swayosd-kbd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-keyboard | `kept` | |
| `nomarchy-sync` | `features/scripts/utils` | README.md,features/scripts/utils/nomarchy-backup | `kept` | |
| `nomarchy-sys-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts/nomarchy-setup-dns, +5 more | `kept` | |
| `nomarchy-sync` | `features/scripts/utils` | README.md,features/scripts/utils/nomarchy-backup, +1 more | `kept` | |
| `nomarchy-sync-nix-state` | `features/scripts/utils` | features/scripts/utils/nomarchy-state-write | `kept` | |
| `nomarchy-sys-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts/nomarchy-setup-dns, +6 more | `kept` | |
| `nomarchy-system-logout` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-system-reboot` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,core/system/scripts/nomarchy-hibernation-setup, +2 more | `kept` | |
| `nomarchy-system-shutdown` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/extensions/menu.sh, +1 more | `kept` | |
@@ -168,22 +166,17 @@ 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-skill/SKILL.md,core/home/config/nomarchy/default/mako/core.ini, +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` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/mako/core.ini, +5 more | `kept` | |
| `nomarchy-update-available` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-day/config.jsonc, +1 more | `kept` | |
| `nomarchy-update-firmware` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-update-time` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-upload-log` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug | `kept` | |
| `nomarchy-version` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug, +1 more | `kept` | |
| `nomarchy-voxtype-config` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-voxtype-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-voxtype-model` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-voxtype-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-voxtype-status` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-wallpaper` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/autostart.conf, +3 more | `kept` | |
| `nomarchy-webapp-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-webapp-remove-all | `kept` | |
| `nomarchy-webapp-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-webapp-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf,features/scripts/utils/nomarchy-installed-summary, +1 more | `kept` | |
| `nomarchy-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf,core/home/config/nomarchy/hooks/post-install.sample, +2 more | `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

@@ -20,7 +20,10 @@ Nomarchy is a NixOS-based distribution characterized by its **Modular Merging Ar
- **Downstream:** A user's installation (typically in `/etc/nixos/`) imports the Upstream flake. The user layers their own `system.nix` and `home.nix` on top, overriding or extending the Upstream settings using native NixOS `lib.mkDefault` and `lib.mkForce` patterns.
### Hybrid Declarative State
While the system is defined declaratively, Nomarchy uses a small, local state file (`~/.config/nomarchy/state.json`) to manage user preferences like the active theme, fonts, and feature toggles. This allows for instant UI feedback (via the `env-update` script) without requiring a full system rebuild for every cosmetic change.
Nomarchy balances the ease of a GUI with the power of declarative configuration.
- **Runtime Discovery:** Local state files (`~/.config/nomarchy/state.json` and `/etc/nixos/state.json`) handle personal choices like theme, font, and UI toggles. Changes made via the Theme Picker or Welcome Wizard update these files and provide instant visual feedback (via the `env-update` script).
- **Declarative Persistence:** To ensure these choices are permanent and reproducible, the `nomarchy-state-sync-nix` script automatically mirrors the home-side configuration into `/etc/nixos/nomarchy-state.nix`. This file is imported by the user's `home.nix`.
- **Solidification:** When you run `nomarchy-env-update`, your current runtime state is "solidified" into the Nix configuration, preventing your settings from reverting to defaults during the next full system rebuild.
---

210
docs/WORKING-ON-NOMARCHY.md Normal file
View File

@@ -0,0 +1,210 @@
# Working on Nomarchy — a practical orientation
This is the "I just want to change something without getting lost" guide. It is
deliberately short. For the full architecture, see [STRUCTURE.md](STRUCTURE.md);
for every option, [OPTIONS.md](OPTIONS.md).
---
## 1. The 60-second mental model
Nomarchy is a NixOS flake. Three layers, top to bottom:
```
core/ foundation → the OS + base user env that must always exist
features/ the desktop → apps, Hyprland/waybar, user scripts (opt-in modules)
themes/ the look → palettes (data) + engine (logic) + templates (.tpl)
```
Everything else is plumbing: `lib/` (shared Nix helpers), `installer/` + `hosts/`
(how it gets onto a disk), `bin/` (repo tooling, **not** shipped to users),
`docs/`.
The flake wires it up as two NixOS/HM modules:
- `nixosModules.system``core/system/` (the OS)
- `nixosModules.home``features/` (which itself imports `core/home/` + the theme engine)
### The two things that confuse everyone
There are **two** places that look like "app config," and they are *different
concepts*:
| Path | What it is |
|------|-----------|
| `features/apps/<app>/` | The **module** for an app — turns it on, sets its config. This is *code*. |
| `core/home/config/nomarchy/default/<app>/` | A **payload** of files copied verbatim to `~/.config/nomarchy/default/<app>/` at rebuild. This is *data* that scripts + the theme engine read at runtime. |
So when you saw `alacritty` in both trees: `features/apps/alacritty/` is the app
module; `core/home/config/nomarchy/default/alacritty/screensaver.toml` is just a
screensaver file that happens to be named after alacritty. Not a duplicate.
**Rule of thumb:** changing how an app *behaves*`features/apps/`. Changing a
file that lands in the user's home and gets read at runtime → `core/home/config/`.
---
## 2. "I want to change X" → go here
| I want to… | Go to |
|------------|-------|
| Tweak an app's settings (kitty, btop, tmux…) | `features/apps/<app>/default.nix` (+ its `config/`) |
| Change Hyprland behaviour / keybinds | `features/desktop/hyprland/` and `core/home/config/nomarchy/default/hypr/` |
| Change the status bar | `features/desktop/waybar/` |
| Add/remove a theme | `themes/palettes/<name>/` |
| Change how theming is applied | `themes/engine/loader.nix` |
| Add a **user** command (`nomarchy-foo`) | `features/scripts/utils/` |
| Add a **system/root** command | `core/system/scripts/` |
| Add a NixOS option (`nomarchy.system.*`) | `core/system/options.nix` |
| Add a HM option (`nomarchy.*`) | `core/home/options.nix` |
| Change what the installer writes | `installer/install.sh` |
When in doubt, grep for an existing example of the thing you're changing and copy
its shape — the repo is very consistent *within* each of these buckets.
---
## 3. The build/test loop
You almost never need a full install to test a change. From the repo root:
```bash
# Evaluate + build the whole system WITHOUT activating it (safe, no sudo):
nixos-rebuild build --flake .#default --impure
# → prints "Done. The new configuration is /nix/store/…" if it builds.
# (drops a ./result symlink you can delete)
```
On a real install, the user-facing commands are:
```bash
sys-update # sudo nixos-rebuild switch --flake .#default --impure (system)
env-update # home-manager switch --flake .#default --impure (user env)
```
`bin/utils/` holds repo tooling that regenerates the auto-docs
(`SCRIPTS.md`, `KEYBINDINGS.md`). A pre-commit hook runs them; you rarely call
them by hand.
---
## 4. Where scripts live (the one rule)
There are **four** script homes, by **execution context**, not by topic:
| Dir | Context | Example |
|-----|---------|---------|
| `bin/utils/` | Repo tooling, never shipped | `nomarchy-docs-scripts` |
| `core/system/scripts/` | System / root / hardware | `nomarchy-setup-dns`, `nomarchy-toggle-hybrid-gpu` |
| `features/scripts/utils/` | User / desktop | `nomarchy-menu`, `nomarchy-launch-walker` |
| `themes/engine/scripts/` | Theme engine | **`nomarchy-theme-set`**, `nomarchy-theme-bg-set`, `nomarchy-theme-next` |
> **Heads-up:** the `nomarchy-theme-*` family lives in `themes/engine/scripts/`, **not**
> `features/scripts/utils/`. All four dirs are built onto the user's `$PATH`, so at
> runtime `nomarchy-theme-set "Tokyo Night"` just works regardless of which dir it's in —
> the split only matters when you're hunting for the *source*. Find any script's source
> fast with `grep -rl nomarchy-theme-set .` rather than guessing the directory.
New script? Ask: *does it need root or system packages?*`core/system/scripts/`.
*Is it user-facing desktop glue?*`features/scripts/utils/`. *Only useful inside
this repo?* → `bin/utils/`. Scripts are found by name on `$PATH`, so moving one
between the first two means changing the Nix derivation it's built into, not the
call sites.
---
## 5. How theming works (the short version)
1. The active theme name lives in **state**: `~/.config/nomarchy/state.json`
(runtime) → mirrored into `/etc/nixos/nomarchy-state.nix` for reproducibility.
2. `themes/engine/loader.nix` reads that name and deploys the matching themed
files (btop theme, kitty colors, waybar css…) into `~/.config/`.
3. `themes/templates/*.tpl` are filled with the palette's colors to produce
dynamic configs.
4. Switching a theme runs scripts that rewrite those files and then *reload* the
affected apps (this is what the `nomarchy-restart-*` family is for — each app
reloads differently: SIGUSR2, a full restart, etc.).
---
## 6. Walker + elephant (so you can decide on the menus later)
This is the bit you wanted to understand before changing anything. There are
**two separate programs**:
- **Walker** — the *front-end*. A Rust/GTK4 window that shows a list and a
search box. It's what you see. Started as a user service
(`programs.walker`, `runAsService = true`).
- **elephant** — the *back-end*. A Go daemon that actually produces the data
(apps, calculator, clipboard, emoji, and **custom menus**). Walker talks to it
over a Unix socket. Config lives in `features/apps/elephant/config/`, deployed
to `~/.config/elephant/`.
Think: **Walker draws, elephant supplies.**
### How a menu reaches the screen
There are two completely different paths, and only one of them touches Lua:
**Path A — `--dmenu` (no elephant data providers, no Lua).**
Used by `nomarchy-menu`, `nomarchy-font`, the keybindings viewer, etc.
You pipe plain text lines into `walker --dmenu`; Walker shows them and prints the
chosen line back. **Text only** — Walker's dmenu mode literally cannot show icons
or an image preview pane (verified in its source: each line becomes `item.text`
and the preview box is hidden). This is the simple, dependency-free path.
```
echo -e "Option A\nOption B" | walker --dmenu -p "Pick…"
```
**Path B — elephant custom menu providers (`-m menus:<name>`).**
Used by the theme picker and wallpaper picker. Here elephant loads a *menu
provider* from `~/.config/elephant/menus/`. A menu provider can be:
- a **TOML** file with a *static* list of entries — each entry can have an
`icon`, a `preview`/`preview_type`, and `actions`; or
- a **Lua** file (`run = "lua:…"`) whose `GetEntries()` returns a *dynamic*
list built at runtime.
The theme/wallpaper menus need a *variable* list (one entry per theme/wallpaper,
discovered on disk) **and** an image preview. In elephant, the only built-in way
to generate a variable-length list is Lua's `GetEntries()`. That's the entire
reason `nomarchy_themes.lua` and `nomarchy_background_selector.lua` exist — and
they're the *only* Lua in the whole repo.
### The decision space (for later)
If you want to drop Lua but keep Walker, the trade is purely about previews:
- **Keep previews** → replace the two `.lua` files with a small **bash generator**
that writes *static* elephant TOML menus (one `[[entries]]` per theme/wallpaper,
each with `preview = "…/preview.png"`), regenerated at rebuild + on theme
switch. No Lua, keeps the preview pane, adds one generator script.
- **Drop previews** → route the theme/wallpaper pickers through `walker --dmenu`
like the other menus. Deletes both `.lua` files, the elephant menu config for
them, and the `pkgs.lua` dependency. Simplest possible; theme/wallpaper become
plain text lists.
Nothing here is decided yet — this section is just the map.
### Key files for the menu system
| File | Role |
|------|------|
| `features/scripts/utils/nomarchy-launch-walker` | Wrapper: starts elephant + walker services, routes `--dmenu` vs provider calls |
| `features/scripts/utils/nomarchy-menu` | The big interactive menu (all Path A / dmenu) |
| `features/apps/walker.nix` | Walker module + config (prefixes, providers, theme) |
| `features/apps/elephant/config/` | elephant providers (calc, symbols, **menus/**) |
| `features/apps/elephant/config/menus/*.lua` | The two dynamic preview menus (the only Lua) |
---
## 7. Gotchas worth knowing
- **`--impure` is required** on rebuilds — the config reads runtime state
(`state.json`) outside the flake.
- **`docs/SCRIPTS.md` and `docs/KEYBINDINGS.md` are auto-generated.** Don't edit by
hand; the pre-commit hook (and CI) regenerate and verify them.
- **The deep `core/home/config/nomarchy/default/…` tree is a payload**, deployed
wholesale to `~/.config/nomarchy/default/`. Moving files out of it will break the
scripts/loader that read those exact runtime paths.
- **Two parallel module systems**: `core/system` = NixOS (root), `features` +
`core/home` = Home Manager (user). A setting only works if it's in the right one.

View File

@@ -67,6 +67,7 @@ in
xdg-terminal-exec
swaybg
rofi # rofi-wayland was merged into rofi upstream
calcurse # TUI calendar
# Theming — cursor package stays here; icon theme packages are pulled in
# dynamically by themes/engine/stylix.nix via nomarchyLib.iconThemePackage

View File

@@ -1,23 +0,0 @@
# See https://wiki.hyprland.org/Configuring/Monitors/
# List current monitors and resolutions possible: hyprctl monitors
# Format: monitor = [port], resolution, position, scale
# Optimized for retina-class 2x displays, like 13" 2.8K, 27" 5K, 32" 6K.
#env = GDK_SCALE,2
#monitor=,preferred,auto,auto
# Good compromise for 27" or 32" 4K monitors (but fractional!)
# env = GDK_SCALE,1.75
# monitor=,preferred,auto,1.6
# Straight 1x setup for low-resolution displays like 1080p or 1440p
# Or for ultrawide monitors like 34" 3440x1440 or 49" 5120x1440
env = GDK_SCALE,1
monitor=,highres,auto,1
# Portrait/rotated secondary monitor (transform: 1 = 90°, 3 = 270°)
# monitor = DP-2, preferred, auto, 1, transform, 1
# Example for Framework 13 w/ 6K XDR Apple display
# monitor = DP-5, 6016x3384@60, auto, 2
# monitor = eDP-1, 2880x1920@120, auto, 2

View File

@@ -53,7 +53,11 @@ in
# 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/monitors.conf".text = lib.mkDefault ''
# Auto-generated by Nomarchy features/desktop/hyprland/default.nix
# monitor = [port], resolution, position, scale
monitor = , highres, auto, ${config.nomarchy.hyprland.scale}
'';
xdg.configFile."hypr/input.conf".source = lib.mkDefault ./config/input.conf;
xdg.configFile."hypr/bindings.conf".source = lib.mkDefault ./config/bindings.conf;

View File

@@ -5,7 +5,7 @@
"spacing": 0,
"height": 26,
"modules-left": ["custom/nomarchy", "hyprland/workspaces"],
"modules-center": ["clock", "custom/update", "custom/voxtype", "custom/screenrecording-indicator", "custom/idle-indicator", "custom/notification-silencing-indicator"],
"modules-center": ["clock", "custom/update", "custom/screenrecording-indicator", "custom/idle-indicator", "custom/notification-silencing-indicator"],
"modules-right": [
"tray",
"bluetooth",
@@ -43,7 +43,7 @@
"format": "",
"on-click": "nomarchy-menu",
"on-click-right": "xdg-terminal-exec",
"tooltip-format": "Nomarchy Menu\n\nSuper + Alt + Space"
"tooltip-format": "Nomarchy Menu\n\nSuper + Shift + Space"
},
"custom/update": {
"format": "",
@@ -58,12 +58,15 @@
"interval": 5,
"format": "󰍛",
"on-click": "nomarchy-launch-or-focus-tui btop",
"on-click-right": "alacritty"
"on-click-right": "alacritty",
"tooltip-format": "Activity\n\nSuper + Ctrl + T"
},
"clock": {
"format": "{:L%A %H:%M}",
"format-alt": "{:L%d %B W%V %Y}",
"tooltip": false,
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T",
"on-click": "nomarchy-launch-or-focus-tui calcurse",
"on-click-right": "nomarchy-launch-floating-terminal-with-presentation nomarchy-tz-select"
},
"network": {
@@ -72,9 +75,9 @@
"format-wifi": "{icon}",
"format-ethernet": "󰀂",
"format-disconnected": "󰤮",
"tooltip-format-wifi": "{essid} ({frequency} GHz)\n⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}",
"tooltip-format-ethernet": "⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}",
"tooltip-format-disconnected": "Disconnected",
"tooltip-format-wifi": "{essid} ({frequency} GHz)\n⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}\n\nSuper + Ctrl + W",
"tooltip-format-ethernet": "⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}\n\nSuper + Ctrl + W",
"tooltip-format-disconnected": "Disconnected\n\nSuper + Ctrl + W",
"interval": 3,
"spacing": 1,
"on-click": "nomarchy-launch-wifi"
@@ -89,8 +92,9 @@
"default": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"]
},
"format-full": "󰂅",
"tooltip-format-discharging": "{power:>1.0f}W↓ {capacity}%",
"tooltip-format-charging": "{power:>1.0f}W {capacity}%",
"tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B",
"tooltip-format-discharging": "{power:>1.0f}W {capacity}%\n\nSuper + Ctrl + Alt + B",
"tooltip-format-charging": "{power:>1.0f}W↑ {capacity}%\n\nSuper + Ctrl + Alt + B",
"interval": 5,
"on-click": "nomarchy-menu power",
"states": {
@@ -104,14 +108,14 @@
"format-disabled": "󰂲",
"format-connected": "󰂱",
"format-no-controller": "",
"tooltip-format": "Devices connected: {num_connections}",
"tooltip-format": "Devices connected: {num_connections}\n\nSuper + Ctrl + B",
"on-click": "nomarchy-launch-bluetooth"
},
"pulseaudio": {
"format": "{icon}",
"on-click": "nomarchy-launch-audio",
"on-click-right": "pamixer -t",
"tooltip-format": "Playing at {volume}%",
"tooltip-format": "Playing at {volume}%\n\nSuper + Ctrl + A",
"scroll-step": 5,
"format-muted": "",
"format-icons": {
@@ -138,19 +142,6 @@
"signal": 10,
"return-type": "json"
},
"custom/voxtype": {
"exec": "nomarchy-voxtype-status",
"return-type": "json",
"format": "{icon}",
"format-icons": {
"idle": "",
"recording": "󰍬",
"transcribing": "󰔟"
},
"tooltip": true,
"on-click-right": "nomarchy-voxtype-config",
"on-click": "nomarchy-voxtype-model"
},
"tray": {
"icon-size": 12,
"spacing": 17

View File

@@ -97,12 +97,3 @@ tooltip {
#custom-notification-silencing-indicator.active {
color: #a55555;
}
#custom-voxtype {
min-width: 12px;
margin: 0 0 0 7.5px;
}
#custom-voxtype.recording {
color: #a55555;
}

View File

@@ -6,11 +6,10 @@
"height": 60,
"layer": "top",
"position": "top",
"output": "DP-2",
"spacing": 15,
"modules-left": ["custom/launcher", "clock", "clock#date"],
"modules-center": ["wlr/workspaces"],
"modules-right": ["pulseaudio", "network", "battery", "custom/powermenu"],
"modules-right": ["custom/update", "pulseaudio", "network", "battery", "custom/powermenu"],
"wlr/workspaces": {
"disable-scroll": true,
@@ -35,12 +34,20 @@
"custom/launcher": {
"interval": "once",
"format": "󰣇",
"on-click": "pkill wofi || wofi --show drun --term=kitty --width=20% --height=50% --columns 1 -I -s ~/.config/wofi/themes/everforest-light.css -o $MAIN_DISPLAY",
"tooltip": false
"on-click": "nomarchy-menu",
"tooltip-format": "Nomarchy Menu\n\nSuper + Shift + Space"
},
"custom/update": {
"format": "",
"exec": "nomarchy-update-available",
"on-click": "nomarchy-launch-floating-terminal-with-presentation nomarchy-update",
"tooltip-format": "Nomarchy update available",
"signal": 7,
"interval": 21600
},
"backlight": {
"device": "nvidia_0",
"max-length": "4",
"format": "{icon}",
"tooltip-format": "{percent}%",
@@ -78,15 +85,17 @@
"car": "",
"default": ["", "", ""]
},
"on-click-right": "pavucontrol",
"on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle"
"on-click": "nomarchy-launch-audio",
"on-click-right": "pamixer -t",
"tooltip-format": "Playing at {volume}%\n\nSuper + Ctrl + A"
},
"network": {
"format-wifi": " {signalStrength}%",
"format-ethernet": " {signalStrength}%",
"format-disconnected": "󰤭",
"on-click": "sh ~/.config/wofi/scripts/wifimenu.sh"
"on-click": "nomarchy-launch-wifi",
"tooltip-format": "Wifi controls\n\nSuper + Ctrl + W"
},
"battery": {
@@ -105,20 +114,26 @@
"format-plugged": " {capacity}%",
"format-alt": "{icon} {capacity}%",
"format-full": " 100%",
"format-icons": ["", "", "", "", "", "", "", "", "", ""]
"format-icons": ["", "", "", "", "", "", "", "", "", ""],
"tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B"
},
"clock": {
"format": " {:%H:%M}"
"format": " {:%H:%M}",
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T"
},
"clock#date": {
"format": " {:%A, %B %d, %Y}"
"format": " {:%A, %B %d, %Y}",
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T",
"on-click": "nomarchy-launch-or-focus-tui calcurse"
},
"custom/powermenu": {
"format": "",
"on-click": "pkill wofi || sh .config/wofi/scripts/powermenu.sh 'everforest-light' '--height=17% -o $MAIN_DISPLAY'",
"on-click": "nomarchy-menu power",
"tooltip": false
}
}

View File

@@ -9,7 +9,7 @@
"spacing": 15,
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
"modules-center": ["hyprland/workspaces"],
"modules-right": [ "idle_inhibitor", "pulseaudio", "custom/battery", "tray", "custom/powermenu"],
"modules-right": ["custom/update", "idle_inhibitor", "pulseaudio", "custom/battery", "tray", "custom/powermenu"],
"hyprland/workspaces": {
"disable-scroll": true,
@@ -40,7 +40,7 @@
"interval": "once",
"format": "\uf000",
"on-click": "nomarchy-menu",
"tooltip-format": "Application Launcher"
"tooltip-format": "Nomarchy Menu\n\nSuper + Shift + Space"
},
"custom/update": {
@@ -73,20 +73,6 @@
"return-type": "json"
},
"custom/voxtype": {
"exec": "nomarchy-voxtype-status",
"return-type": "json",
"format": "{icon}",
"format-icons": {
"idle": "",
"recording": "󰍬",
"transcribing": "󰔟"
},
"tooltip": true,
"on-click-right": "nomarchy-voxtype-config",
"on-click": "nomarchy-voxtype-model"
},
"backlight": {
"max-length": "4",
"format": "{icon} {percent}%",
@@ -130,36 +116,41 @@
"on-click": "pamixer -t",
"on-click-middle": "nomarchy-launch-audio",
"on-click-right": "pavucontrol",
"tooltip-format": "{icon} {desc} | {volume}%"
"tooltip-format": "{icon} {desc} | {volume}%\n\nSuper + Ctrl + A"
},
"network": {
"format-wifi": " {signalStrength}%",
"format-ethernet": " {signalStrength}%",
"format-disconnected": "󰤭",
"on-click": "nomarchy-launch-wifi"
"on-click": "nomarchy-launch-wifi",
"tooltip-format": "Wifi controls\n\nSuper + Ctrl + W"
},
"custom/battery": {
"interval": 30,
"format": "{}",
"exec": "nomarchy-battery-status",
"on-click": "nomarchy-menu power"
"on-click": "nomarchy-menu power",
"tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B"
},
"clock": {
"format": " {:%H:%M}",
"tooltip": false
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T"
},
"clock#date": {
"format": " {:%A, %B %d, %Y}",
"tooltip": false
"tooltip": true,
"tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T",
"on-click": "nomarchy-launch-or-focus-tui calcurse"
},
"custom/powermenu": {
"format": "",
"on-click": "wlogout",
"on-click": "nomarchy-menu power",
"tooltip": false
}
}

View File

@@ -33,3 +33,4 @@ else
fi
echo "Environment update complete."
nomarchy-hook post-update

View File

@@ -22,4 +22,8 @@ esac
hyprctl keyword misc:disable_scale_notification true
hyprctl keyword monitor "$ACTIVE_MONITOR,${WIDTH}x${HEIGHT}@${REFRESH_RATE},auto,$NEW_SCALE"
hyprctl keyword misc:disable_scale_notification false
# Persist the choice declaratively
nomarchy-state-write "hyprland.scale" "$NEW_SCALE"
notify-send -u low "󰍹 Display scaling set to ${NEW_SCALE}x"

View File

@@ -25,7 +25,6 @@ else
hyprctl keyword general:border_size 0
fi
TMP_JSON=$(mktemp)
jq --argjson state "$NEW_STATE" '.hyprland = $state' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write hyprland "$NEW_STATE" --type json
echo "Toggled gaps to $NEW_STATE declaratively."

View File

@@ -8,12 +8,20 @@ if command -v walker >/dev/null 2>&1; then
# /dev/null. If they inherit stdout from a $(...) caller (e.g. nomarchy-menu
# doing `$(menu ...)`), bash waits for those fds to close on every return,
# which hangs the terminal after each menu selection.
if ! pgrep -x elephant > /dev/null; then
# Prefer the Home Manager systemd user services (programs.walker.runAsService).
# The manual fallbacks below MUST check the services first: the service runs
# the wrapped binary `.elephant-wrapped`, which `pgrep -x elephant` never
# matches — so the old check spawned a *second*, competing elephant on every
# menu invocation, racing the service for the socket. Only hand-start when no
# service and no process exists (e.g. walker used outside the HM service).
if ! systemctl --user is-active --quiet elephant.service 2>/dev/null \
&& ! pgrep -x elephant >/dev/null && ! pgrep -f elephant-wrapped >/dev/null; then
setsid uwsm-app -- elephant </dev/null >/dev/null 2>&1 &
disown
fi
if ! pgrep -f "walker --gapplication-service" > /dev/null; then
if ! systemctl --user is-active --quiet walker.service 2>/dev/null \
&& ! pgrep -f "walker --gapplication-service" >/dev/null; then
setsid uwsm-app -- walker --gapplication-service </dev/null >/dev/null 2>&1 &
disown
fi

View File

@@ -9,8 +9,20 @@ if (($# == 0)); then
fi
# Find the first notification whose 'summary' matches the regex in $1
notification_id=$(makoctl list | grep -F "$1" | head -n1 | sed -E 's/^Notification ([0-9]+):.*/\1/')
output=$(makoctl list)
if [[ -n $notification_id ]]; then
makoctl dismiss -n $notification_id
if [[ $output == "["* ]] || [[ $output == "{"* ]]; then
# JSON format (mako 1.8+)
# Structure is usually an array of notifications or a wrapper object
notification_id=$(echo "$output" | jq -r --arg pat "$1" '
if type == "array" then .
else .notifications // .data[0] // [] end
| .[] | select((.summary // .["summary-text"] // "") | contains($pat)) | .id' | head -n1)
else
# Human format (older mako)
notification_id=$(echo "$output" | grep -F "$1" | head -n1 | sed -E 's/^Notification ([0-9]+):.*/\1/')
fi
if [[ "$notification_id" =~ ^[0-9]+$ ]]; then
makoctl dismiss -n "$notification_id"
fi

View File

@@ -1,9 +0,0 @@
#!/usr/bin/env bash
set -e
# Nomarchy AUR Stub
# Informs the user that AUR is not applicable to NixOS.
notify-send -u critical "Nomarchy" "The AUR is an Arch Linux feature and is not available on NixOS. Try searching for the package in nixpkgs using 'nix search nixpkgs <name>'."
echo "Error: AUR is not available on NixOS."
exit 1

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
# Nomarchy Package Drop Script
# Alias for nomarchy-pkg-remove.
nomarchy-pkg-remove "$@"

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
# Nomarchy Package Install Script
# Alias for nomarchy-pkg-add for users coming from other distros.
nomarchy-pkg-add "$@"

View File

@@ -90,12 +90,18 @@ LOCK_FILE="${STATE_FILE}.lock"
# Validate JSON before moving
if jq empty "$TMP_FILE" 2>/dev/null; then
mv "$TMP_FILE" "$STATE_FILE"
# Sync to the Nix configuration while still holding the lock.
# This prevents desyncs where a newer JSON state is overwritten by an
# older Nix state during concurrent writes.
if [[ "$STATE_FILE" == "$HOME/.config/nomarchy/state.json" ]]; then
if command -v nomarchy-sync-nix-state >/dev/null 2>&1; then
nomarchy-sync-nix-state
fi
fi
else
echo "Error: Failed to generate valid JSON"
exit 1
fi
) 200>"$LOCK_FILE"
# Clean up lock file
rm -f "$LOCK_FILE"

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -e
# Nomarchy Nix State Sync
# Bridges the gap between runtime UI changes (state.json) and declarative
# Nix configuration (nomarchy-state.nix).
STATE_JSON="$HOME/.config/nomarchy/state.json"
NIX_STATE_FILE="/etc/nixos/nomarchy-state.nix"
# If state.json is missing, nothing to sync
if [ ! -f "$STATE_JSON" ]; then
exit 0
fi
# Determine destination.
# If we are in the repo, we sync to ./nomarchy-state.nix (dev mode).
# Otherwise, we sync to /etc/nixos/nomarchy-state.nix (real install).
if [ -f "flake.nix" ] || [ -d ".git" ]; then
DEST="./nomarchy-state.nix"
elif [ -w "/etc/nixos" ]; then
DEST="$NIX_STATE_FILE"
else
# Nowhere to sync to
exit 0
fi
# Generate the Nix attribute set from JSON.
# We use jq to extract the keys and format them as Nix assignments.
# Note: font in state.json maps to fonts.monospace in Nix.
{
echo "# DO NOT EDIT MANUALLY - Managed by Nomarchy scripts."
echo "# This file mirrors your UI choices (theme, font, etc.) into the declarative"
echo "# Nix configuration. It is imported by your home.nix."
echo "{ lib, ... }:"
echo "{"
echo " nomarchy = {"
# Core UI strings
echo " theme = lib.mkDefault \"$(jq -r '.theme // empty' "$STATE_JSON")\";"
echo " fonts.monospace = lib.mkDefault \"$(jq -r '.font // empty' "$STATE_JSON")\";"
echo " panelPosition = lib.mkDefault \"$(jq -r '.panelPosition // empty' "$STATE_JSON")\";"
echo " wallpaper = lib.mkDefault \"$(jq -r '.wallpaper // empty' "$STATE_JSON")\";"
# Hyprland layout (numbers)
echo " hyprland = {"
echo " scale = lib.mkDefault \"$(jq -r '.hyprland.scale // "auto"' "$STATE_JSON")\";"
echo " gaps_in = lib.mkDefault $(jq -r '.hyprland.gaps_in // 5' "$STATE_JSON");"
echo " gaps_out = lib.mkDefault $(jq -r '.hyprland.gaps_out // 10' "$STATE_JSON");"
echo " border_size = lib.mkDefault $(jq -r '.hyprland.border_size // 2' "$STATE_JSON");"
echo " };"
# Toggles (booleans)
echo " toggles = {"
echo " suspend = lib.mkDefault $(jq -r '.suspend // true' "$STATE_JSON");"
echo " screensaver = lib.mkDefault $(jq -r '.screensaver // true' "$STATE_JSON");"
echo " idle = lib.mkDefault $(jq -r '.idle // true' "$STATE_JSON");"
echo " nightlight = lib.mkDefault $(jq -r '.nightlight // false' "$STATE_JSON");"
echo " waybar = lib.mkDefault $(jq -r '.waybar // true' "$STATE_JSON");"
echo " };"
echo " };"
echo "}"
} > "$DEST.tmp"
# Final atomic move
mv "$DEST.tmp" "$DEST"

View File

@@ -19,8 +19,7 @@ else
notify-send -u low " Screensaver disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.screensaver = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write screensaver "$NEW_VALUE" --type bool
echo "Screensaver state set to $NEW_VALUE. Updating environment..."

View File

@@ -22,7 +22,6 @@ else
notify-send -u low " Top bar disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.waybar = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write waybar "$NEW_VALUE" --type bool
echo "Waybar state set to $NEW_VALUE."

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Nomarchy Full Update Script
# 1. Removes the installation pin (if present) to follow the rolling release.
# 2. Updates flake inputs (Nomarchy, nixpkgs, etc.) in flake.lock.
# 3. Performs a full system update (NixOS + Home Manager)
set -e
# Fast fail offline check for gum
if ! ping -c 1 -W 2 1.1.1.1 >/dev/null 2>&1; then
if command -v gum >/dev/null 2>&1; then
gum style --foreground 196 "You are offline. Cannot update."
sleep 3
else
echo "You are offline. Cannot update."
fi
exit 1
fi
# Detect the repository location
if [ -f "/etc/nixos/flake.nix" ]; then
REPO_DIR="/etc/nixos"
elif [ -f "/etc/nomarchy/flake.nix" ]; then
REPO_DIR="/etc/nomarchy"
else
echo "Error: Nomarchy flake repository not found in /etc/nixos or /etc/nomarchy."
exit 1
fi
CURRENT_REV=$(jq -r '.nodes.nomarchy.locked.rev // empty' "$REPO_DIR/flake.lock" 2>/dev/null)
REMOTE_URL="https://git.bemagri.xyz/bernardo/Nomarchy.git"
LATEST_REV=$(git ls-remote "$REMOTE_URL" HEAD 2>/dev/null | awk '{print $1}')
if command -v gum >/dev/null 2>&1; then
gum style --foreground 212 --border double --margin "1 2" --padding "1 2" "Nomarchy System Update"
if [[ "$CURRENT_REV" != "$LATEST_REV" && -n "$LATEST_REV" ]]; then
echo -e "An update for Nomarchy is available!\n"
echo -e "Current Revision: ${CURRENT_REV:0:7}"
echo -e "Latest Revision: ${LATEST_REV:0:7}\n"
if ! gum confirm "Do you want to download and apply the update now?"; then
echo "Update cancelled."
sleep 2
exit 0
fi
else
echo -e "\nYou are already up to date! (${CURRENT_REV:0:7})"
if ! gum confirm "Do you want to force a system rebuild anyway?"; then
echo "Cancelled."
sleep 2
exit 0
fi
fi
fi
echo "--- Starting Nomarchy Full Update ---"
# 1. Unpin the flake if it's currently pinned to the install commit
if grep -q "rev=" "$REPO_DIR/flake.nix"; then
echo "Removing installation pin to follow rolling release..."
sudo sed -i 's/?rev=[a-f0-9]*//g' "$REPO_DIR/flake.nix"
fi
# 2. Update flake inputs
echo "Updating flake inputs (this may take a moment)..."
sudo nix --extra-experimental-features 'nix-command flakes' flake update --flake "$REPO_DIR"
# 3. System update
nomarchy-sys-update
echo "--- Nomarchy Full Update Complete ---"
if command -v gum >/dev/null 2>&1; then
echo ""
gum style --foreground 82 "Update Applied Successfully!"
echo "Press any key to exit."
read -n 1 -s -r
fi

View File

@@ -16,18 +16,42 @@ if [ -z "$REPO_DIR" ]; then
exit 0
fi
# We use a simple logic: Check for flake updates periodically.
# Since this is run by Waybar, we should be careful with performance.
# Fast-fail if offline to avoid expensive nix commands.
if ! ping -c 1 -W 2 1.1.1.1 >/dev/null 2>&1; then
exit 0
fi
# For a quick check, we can see if there are newer versions available for nixpkgs
# by checking nix flake metadata on the repo.
# Get the current locked revision for Nomarchy
LOCKED_REV=$(jq -r '.nodes.nomarchy.locked.rev // empty' "$REPO_DIR/flake.lock" 2>/dev/null)
# Get current status
CURRENT_REV=$(nix flake metadata "$REPO_DIR" --json | jq -r '.lock.nodes.root.inputs.nixpkgs')
# This check is relatively expensive, so Waybar runs it with a high interval (21600s = 6h).
if [ -z "$LOCKED_REV" ]; then
# Could be named something else or missing, fallback to generic icon
echo ""
exit 0
fi
# Just return an icon if we are in a system that can be updated.
# In a real implementation, we could compare local flake.lock vs upstream if it's a git repo.
# For now, we'll return the update icon to show it's active.
# Check if we are pinned in flake.nix
if grep -q "rev=" "$REPO_DIR/flake.nix" 2>/dev/null; then
# We are pinned. An update implies unpinning and upgrading.
# We can just show the icon to encourage the user to update and unpin.
echo ""
exit 0
fi
echo ""
# Get remote latest revision
REMOTE_URL="https://git.bemagri.xyz/bernardo/Nomarchy.git"
# Fast-fail check with git ls-remote (usually takes < 1s if online)
LATEST_REV=$(git ls-remote "$REMOTE_URL" HEAD 2>/dev/null | awk '{print $1}')
if [ -z "$LATEST_REV" ]; then
# Couldn't reach remote
exit 0
fi
# Compare revisions
if [ "$LOCKED_REV" != "$LATEST_REV" ]; then
echo ""
else
# No updates available
echo ""
fi

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Nomarchy Time Sync Script
# Force sync system time using systemd-timesyncd
set -e
echo "Syncing system time..."
if ! sudo systemctl restart systemd-timesyncd; then
echo "Error: Failed to restart systemd-timesyncd. Check internet connection."
exit 1
fi
# Wait a moment for sync
sleep 2
echo "Current time: $(date)"
echo "Time sync complete."

View File

@@ -13,9 +13,15 @@ fi
# Use ix.io as the default pastebin
if [[ -n $1 ]]; then
url=$(curl -F "f:1=@$1" ix.io)
# --connect-timeout 5: fail fast if ix.io is unreachable
url=$(curl -s --connect-timeout 5 -F "f:1=@$1" ix.io || echo "OFFLINE")
else
url=$(curl -F "f:1=@-" ix.io)
url=$(curl -s --connect-timeout 5 -F "f:1=@-" ix.io || echo "OFFLINE")
fi
if [[ "$url" == "OFFLINE" ]]; then
echo "Error: Could not upload log. Check your internet connection."
exit 1
fi
echo "Log uploaded to: $url"

View File

@@ -1,6 +0,0 @@
#!/bin/bash
set -e
# Used by Voxtype waybar module to open config on right click
exec nomarchy-launch-editor ~/.config/voxtype/config.toml

View File

@@ -1,21 +0,0 @@
#!/bin/bash
set -e
# Install voxtype and configure it for use.
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
nomarchy-pkg-add wtype voxtype-bin
# Setup voxtype
mkdir -p ~/.config/voxtype
cp ~/.config/nomarchy/default/voxtype/config.toml ~/.config/voxtype/
voxtype setup --download --no-post-install
if nomarchy-hw-vulkan; then
voxtype setup gpu --enable || true
fi
voxtype setup systemd
nomarchy-restart-waybar
notify-send " Voxtype Dictation Ready" "Press Super + Ctrl + X to toggle dictation.\nEdit ~/.config/voxtype/config.toml for options." -t 10000
fi

View File

@@ -1,5 +0,0 @@
#!/bin/bash
set -e
nomarchy-launch-floating-terminal-with-presentation "voxtype setup model"
nomarchy-restart-waybar

View File

@@ -1,20 +0,0 @@
#!/bin/bash
set -e
# Remove voxtype and its configurations.
if nomarchy-cmd-present voxtype; then
echo "Uninstall Voxtype to remove dictation."
# Remove services
systemctl --user stop voxtype.service 2>/dev/null || true
rm -f ~/.config/systemd/user/voxtype*
systemctl --user daemon-reload
# Remove packages and configs
nomarchy-pkg-drop wtype voxtype-bin
rm -rf ~/.config/voxtype
rm -rf ~/.local/share/voxtype
else
echo "Voxtype was not installed."
fi

View File

@@ -1,10 +0,0 @@
#!/bin/bash
set -e
if nomarchy-cmd-present voxtype; then
voxtype status --follow --extended --format json | while read -r line; do
echo "$line" | jq -c '. + {alt: .class}'
done
else
echo '{"alt": "", "tooltip": ""}'
fi

View File

@@ -56,6 +56,15 @@ if [[ -n "$POSITION" ]]; then
nomarchy-state-write panelPosition "$POSITION"
fi
# 4. Select monitor scaling
echo "Step 4: Choose your monitor scaling"
echo "Recommended: 'auto' for most screens, '2' for 4K/HiDPI."
SCALE=$(gum choose "auto" "1" "1.25" "1.5" "2")
if [[ -n "$SCALE" ]]; then
nomarchy-state-write "hyprland.scale" "$SCALE"
fi
# Skip system-modifying steps in the Live ISO environment
if [[ "$USER" == "nixos" ]]; then
echo ""
@@ -90,4 +99,5 @@ nomarchy-state-write welcome_done true --type bool
rm -f "$HOME/.config/nomarchy/.first-run-done"
gum style --foreground 82 "Setup complete! Enjoy your Nomarchy experience."
nomarchy-hook post-install
sleep 3

View File

@@ -211,10 +211,8 @@
# Live-ISO-only welcome. Pops a notification a few seconds
# after the graphical session is up and opens a terminal
# parked at the installer command, so the user never has to
# hunt for it. Force highres monitor detection as some
# hardware defaults to 1024x768.
# hunt for it.
wayland.windowManager.hyprland.extraConfig = nixpkgs.lib.mkAfter ''
monitor = , highres, auto, 1
exec-once = sh -c 'sleep 3; notify-send -u critical -t 0 "Welcome to Nomarchy" "Run \`sudo /etc/install.sh\` in the open terminal or \`--dry-run\` to preview."'
exec-once = sh -c 'sleep 4; alacritty --title "Nomarchy Installer" -e bash -lc "echo; echo \"Welcome to the Nomarchy live ISO.\"; echo; echo \" sudo /etc/install.sh # install\"; echo \" sudo /etc/install.sh --dry-run # preview only\"; echo \" sudo /etc/install.sh --resume # resume after an interrupt\"; echo; exec bash"'

View File

@@ -49,6 +49,14 @@
];
services.xserver.videoDrivers = [ "amdgpu" "radeon" "nouveau" "modesetting" "fbdev" "virtio" ];
# VM guest agents. Without these the virtio-gpu connector only advertises
# its 1024x768 fallback mode, so Hyprland's `monitor = , highres, …` rule
# picks that as the highest available resolution and the live session boots
# tiny. The agents let the SPICE/QEMU host negotiate a real resolution.
# (The installed system gets the same wiring via core/system/vm-guest.nix.)
services.spice-vdagentd.enable = true;
services.qemuGuest.enable = true;
environment.etc."install.sh" = {
source = ../installer/install.sh;
mode = "0755";
@@ -81,6 +89,13 @@
# Allow passwordless sudo for the live user
security.sudo.wheelNeedsPassword = false;
# The live accounts have empty passwords, but pam_unix rejects an empty
# password by default — which leaves hyprlock unsolvable if the idle
# timer locks the session (nothing you can type unlocks it). Allow null
# passwords for the locker so it can be dismissed with a bare Enter.
# Live-ISO only: installed systems must never accept empty unlocks.
security.pam.services.hyprlock.allowNullPassword = true;
# Override the upstream installer helpLine (says "NixOS", points nowhere
# useful for us). Shown on every TTY before login and again as the MOTD.
services.getty.helpLine = lib.mkForce ''

View File

@@ -257,9 +257,14 @@ check_environment() {
fi
success "UEFI boot detected"
# Find Nomarchy repo
# Find Nomarchy repo. Resolve symlinks: on the live ISO /etc/nomarchy is a
# symlink chain (/etc/nomarchy -> /etc/static/nomarchy -> /nix/store/…-source).
# `builtins.getFlake` (used below to load nixpkgs.lib for the state-schema
# eval) rejects a symlink path with "path '…-source' is a symlink" on Nix
# 2.31+, which aborted every real-hardware install at "Creating system
# configuration". Pass it the resolved store directory instead.
if [[ -d "/etc/nomarchy" ]]; then
NOMARCHY_REPO="/etc/nomarchy"
NOMARCHY_REPO="$(realpath /etc/nomarchy)"
elif [[ -d "$(dirname "$0")/.." ]] && [[ -f "$(dirname "$0")/../flake.nix" ]]; then
NOMARCHY_REPO="$(realpath "$(dirname "$0")/..")"
fi
@@ -1462,6 +1467,42 @@ generate_flake_config() {
impermanence_opt=$'nomarchy.system.impermanence.enable = true;\n nomarchy.system.impermanence.mainLuksName = "'"$_main_luks_name"$'";\n nomarchy.system.impermanence.user = "'"$USERNAME"$'";'
fi
# Multi-disk LUKS unlock. `nixos-generate-config` only emits a
# boot.initrd.luks.devices entry for the container that backs a
# fileSystems device — and every subvolume mount points at the main
# /dev/mapper/crypted_main. The extra drives in a multi-disk BTRFS array
# are members of that filesystem but are invisible through the
# device-mapper path it traces, so it never declares them. Without the
# entries below, initrd unlocks only the main drive, the multi-device
# BTRFS can never be assembled, and the installed system hangs at boot
# (the failure is invisible single-disk and only bites multi-disk
# hardware). We also re-assert the x-systemd.requires mount options disko
# set on every BTRFS mount (installer/disko-config.nix) but which
# nixos-generate-config drops — they make systemd-initrd wait for ALL
# members before mounting. Drive→mapper/partlabel naming mirrors
# disko-config.nix's `sanitize` (replace '/' and '-' with '_').
local EXTRA_LUKS_OPTS=""
local _ld=($TARGET_DRIVE)
if (( ${#_ld[@]} > 1 )); then
local _luks_lines="" _requires="" _i _extra _san _mapper _partlabel _uuid
for (( _i=1; _i<${#_ld[@]}; _i++ )); do
_extra="${_ld[$_i]}"
_san="${_extra//\//_}"; _san="${_san//-/_}" # /dev/sdb -> _dev_sdb
_mapper="crypted_${_san}"
_partlabel="disk-extra_${_san}-luks"
_uuid=$(blkid -s UUID -o value "/dev/disk/by-partlabel/$_partlabel")
_luks_lines+=$'\n'" boot.initrd.luks.devices.\"$_mapper\".device = \"/dev/disk/by-uuid/$_uuid\";"
_luks_lines+=$'\n'" boot.initrd.luks.devices.\"$_mapper\".allowDiscards = true;"
_requires+=" \"x-systemd.requires=/dev/mapper/$_mapper\""
done
# BTRFS subvolume mountpoints — must match installer/disko-config.nix.
local _mp _fs_lines=""
for _mp in / /persist /home /nix /var/log /.snapshots; do
_fs_lines+=$'\n'" fileSystems.\"$_mp\".options = [$_requires ];"
done
EXTRA_LUKS_OPTS="$_luks_lines"$'\n'"$_fs_lines"
fi
local PROFILE_SYSTEM_OPTS=""
local PROFILE_HOME_PACKAGES=""
@@ -1584,7 +1625,7 @@ FLAKE_EOF
imports = [
$HARDWARE_MODULES
];
$NOMARCHY_HW_OPTS
$NOMARCHY_HW_OPTS$EXTRA_LUKS_OPTS
}
EOF
@@ -1699,9 +1740,6 @@ $xkb_variant_line
# fcitx5 input method (CJK / IME).
# nomarchy.system.inputMethod.enable = true;
# voxtype voice-typing wiring (you must install voxtype yourself).
# nomarchy.system.voxtype.enable = true;
system.stateVersion = "25.11";
}
EOF
@@ -1714,6 +1752,12 @@ EOF
cat > /mnt/etc/nixos/home.nix << EOF
{ pkgs, ... }:
{
imports = [
# Machine-managed state (theme, font, toggles).
# UI scripts update this file automatically.
./nomarchy-state.nix
];
# Physical form factor — mirrors nomarchy.system.formFactor in system.nix.
# Gates UI affordances like the waybar battery widget.
nomarchy.formFactor = "$FORM_FACTOR";
@@ -1854,6 +1898,46 @@ EOF
state = schema.system // { timezone = \"$_state_tz\"; };
in builtins.toJSON state
" | nrun jq '.' > /mnt/etc/nixos/state.json
# nomarchy-state.nix — the declarative counterpart to state.json.
# Imported by home.nix so UI changes are solidified in code.
# Generated from lib/state-schema.nix's home block.
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; };
s = schema.home;
in ''
# DO NOT EDIT MANUALLY - Managed by Nomarchy scripts.
# This file mirrors your UI choices (theme, font, etc.) into the declarative
# Nix configuration. It is imported by your home.nix.
{ lib, ... }:
{
nomarchy = {
theme = lib.mkDefault \"\${s.theme}\";
fonts.monospace = lib.mkDefault \"\${s.font}\";
panelPosition = lib.mkDefault \"\${s.panelPosition}\";
wallpaper = lib.mkDefault \"\${s.wallpaper}\";
hyprland = {
scale = lib.mkDefault \"\${s.hyprland.scale}\";
gaps_in = lib.mkDefault \${toString s.hyprland.gaps_in};
gaps_out = lib.mkDefault \${toString s.hyprland.gaps_out};
border_size = lib.mkDefault \${toString s.hyprland.border_size};
};
toggles = {
suspend = lib.mkDefault \${lib.boolToString s.suspend};
screensaver = lib.mkDefault \${lib.boolToString s.screensaver};
idle = lib.mkDefault \${lib.boolToString s.idle};
nightlight = lib.mkDefault \${lib.boolToString s.nightlight};
waybar = lib.mkDefault \${lib.boolToString s.waybar};
};
};
}
''
" > /mnt/etc/nixos/nomarchy-state.nix
}
# ============================================================================

View File

@@ -27,11 +27,11 @@
nightlight = false;
waybar = true;
# Hyprland window manager settings
hyprland = {
gaps_in = 5;
gaps_out = 10;
border_size = 2;
scale = "auto"; # Default monitor scale. Options: "auto", "1", "1.25", "1.5", "2"
};
};

View File

@@ -106,21 +106,44 @@ in
source = themeAppsPath + "/rofi.rasi";
};
# Walker fallback support
xdg.configFile."nomarchy/current/theme/apps/walker/style.css" = lib.mkIf (!builtins.pathExists (themeAppsPath + "/walker/style.css")) {
text = ''
* {
color: #${palette.base05};
}
#window {
# Dynamically generated CSS for Walker app launcher
xdg.configFile."nomarchy/current/theme/apps/walker/style.css".text = ''
#window {
background-color: #${palette.base00};
}
.item.active {
background-color: #${palette.base03};
color: #${palette.base0B};
}
'';
};
color: #${palette.base05};
border-bottom: 5px solid #${palette.base0D}; /* Accent */
border-radius: 15px;
}
#search {
background-color: #${palette.base01};
color: #${palette.base05};
border-bottom: 5px solid #${palette.base02};
border-radius: 10px;
margin: 15px;
padding: 10px;
}
#input {
color: #${palette.base05};
}
#list {
padding: 20px;
}
.item {
border-radius: 10px;
padding: 10px;
color: #${palette.base05};
}
.item.active {
background-color: #${palette.base02};
color: #${palette.base05};
border-bottom: 5px solid #${palette.base0D}; /* Accent */
}
'';
xdg.configFile."nomarchy/current/theme.name".text = config.nomarchy.theme;

View File

@@ -24,9 +24,8 @@ mkdir -p "$STATE_DIR"
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
if fc-list | grep -iq "$font_name"; then
TMP_JSON=$(mktemp)
jq --arg font "$font_name" '.font = $font' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
echo "Font set to $font_name declaratively. Applying changes..."
nomarchy-state-write font "$font_name"
echo "Font set to $font_name. Applying changes with nomarchy-env-update..."
if [[ "$no_update" == "true" ]]; then
echo "Skipping nomarchy-env-update due to --no-update flag."

View File

@@ -45,8 +45,7 @@ done
NEXT_INDEX=$(((INDEX + 1) % TOTAL))
NEW_BG="$BG_DIR/${BACKGROUNDS[$NEXT_INDEX]}"
TMP_JSON=$(mktemp)
jq --arg wp "$NEW_BG" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write wallpaper "$NEW_BG"
# Instant feedback via swww
if pgrep -x swww-daemon >/dev/null; then

View File

@@ -28,8 +28,7 @@ mkdir -p "$STATE_DIR"
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
# Persist the choice for the next rebuild.
TMP_JSON=$(mktemp)
jq --arg wp "$BACKGROUND" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write wallpaper "$BACKGROUND"
# Create symlink to the new background
ln -nsf "$BACKGROUND" "$CURRENT_BACKGROUND_LINK"

View File

@@ -34,13 +34,12 @@ if [ ! -d "$THEMES_DIR/$THEME_NAME" ]; then
exit 1
fi
TMP_JSON=$(mktemp)
jq --arg theme "$THEME_NAME" '.theme = $theme' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write theme "$THEME_NAME"
# Sync to system state if we have permissions (for system-level theming like browser policies)
SYSTEM_STATE_FILE="/etc/nixos/state.json"
if [ -w "$SYSTEM_STATE_FILE" ] || [ -w "/etc/nixos" ]; then
tmp=$(mktemp); sudo jq --arg theme "$THEME_NAME" '.theme = $theme' "$SYSTEM_STATE_FILE" > "$tmp" 2>/dev/null && sudo mv "$tmp" "$SYSTEM_STATE_FILE" 2>/dev/null || rm -f "$tmp"
nomarchy-state-write --file "$SYSTEM_STATE_FILE" theme "$THEME_NAME"
fi
# Try to find a background for this theme
@@ -48,8 +47,7 @@ BG_DIR="$THEMES_DIR/$THEME_NAME/backgrounds"
if [ -d "$BG_DIR" ]; then
BG=$(ls "$BG_DIR" | head -n 1)
if [ -n "$BG" ]; then
TMP_JSON=$(mktemp)
jq --arg wp "$BG_DIR/$BG" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write wallpaper "$BG_DIR/$BG"
fi
fi

View File

@@ -25,7 +25,6 @@ else
notify-send -u low " Nightlight disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.nightlight = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
nomarchy-state-write nightlight "$NEW_VALUE" --type bool
echo "Nightlight state set to $NEW_VALUE."

View File

@@ -7,7 +7,7 @@ configuration {
modi: "drun";
show-icons: true;
icon-theme: "Papirus-Light";
terminal: "kitty";
terminal: "alacritty";
drun-display-format: "{name}";
case-sensitive: false;
location: 0;

View File

@@ -1,34 +0,0 @@
#window {
background-color: #fdf6e3;
color: #5c6a72;
border-bottom: 5px solid #dfa000;
border-radius: 15px;
}
#search {
background-color: #8da101;
color: #fdf6e3;
border-bottom: 5px solid #bdc3af;
border-radius: 10px;
margin: 15px;
padding: 10px;
}
#input {
color: #5c6a72;
}
#list {
padding: 20px;
}
.item {
border-radius: 10px;
padding: 10px;
}
.item.active {
background-color: #e6e2cc;
color: #5c6a72;
border-bottom: 5px solid #bdc3af;
}

View File

@@ -7,7 +7,7 @@ configuration {
modi: "drun";
show-icons: true;
icon-theme: "Papirus-Dark";
terminal: "kitty";
terminal: "alacritty";
drun-display-format: "{name}";
case-sensitive: false;
location: 0;

View File

@@ -1,34 +0,0 @@
#window {
background-color: #2d353b;
color: #d3c6aa;
border-bottom: 5px solid #d9bb80;
border-radius: 15px;
}
#search {
background-color: #a7c080;
color: #2d353b;
border-bottom: 5px solid #868d80;
border-radius: 10px;
margin: 15px;
padding: 10px;
}
#input {
color: #2d353b;
}
#list {
padding: 20px;
}
.item {
border-radius: 10px;
padding: 10px;
}
.item.active {
background-color: #3d484d;
color: #d3c6aa;
border-bottom: 5px solid #161a1d;
}