diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml deleted file mode 100644 index c88059f..0000000 --- a/.gitea/workflows/check.yml +++ /dev/null @@ -1,190 +0,0 @@ -# Nomarchy CI — eval + lint. -# -# Catches the regressions that hurt today: -# 1. Flake stops evaluating (broken option ref, missing import, etc.). -# Checked per-output rather than via `nix flake check` so we can skip -# packages.allThemeVariants — the 22-generation linkFarm that OOMs the -# runner; every palette is validated one-at-a-time by the matrix (4). -# 2. A `nomarchy-*` shell script has a syntax error or a shellcheck -# error-severity issue. -# 3. `docs/SCRIPTS.md` drifts from the repo state because somebody -# added / removed / renamed a script and didn't run the generator -# (the pre-commit hook handles this, but only when enabled per-clone). -# 4. An opt-in nomarchy.* toggle stops evaluating. `nix flake check` -# only evaluates the four default configs, so a bug that only fires -# when a toggle is flipped sails through (e.g. the impermanence -# systemd-stage-1 assertion). nomarchy-eval-matrix layers each toggle -# onto the default config and forces system.build.toplevel. -# -# Doesn't build ISOs — that needs a binary cache. Add a separate job -# once Cachix/Attic is in place. - -name: Check - -on: - push: - branches: [main] - pull_request: - -jobs: - eval-and-lint: - runs-on: ubuntu-latest - # Flakes for every `nix` invocation in the job (the bare `nix flake - # check` below relies on this; the scripts pass the flag themselves). - # sandbox=false because Stylix/base16.nix do import-from-derivation — - # eval realizes fetched `-source` paths mid-check, and the single-user - # Nix in this container can't set up the build sandbox (no user - # namespaces), which otherwise surfaces as "path '…-source' is not - # valid". NIX_SSL_CERT_FILE because we add nix to PATH without sourcing - # the installer's profile (which is what normally exports it). - env: - NIX_CONFIG: | - experimental-features = nix-command flakes - sandbox = false - NIX_SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Nix - # Plain shell install rather than a JS action: act_runner's bundled - # `act` only supports up to node20, and nix-installer-action@main - # moved to node24 ("runs.using ... got node24"). A run step has no - # node-runtime coupling. Single-user (--no-daemon) needs no systemd, - # which the catthehacker container doesn't run as PID 1. - # - # The installer runs as root here and honours build-users-group= - # nixbld from its bundled nix.conf, aborting unless that group exists - # AND has members. Create the nixbld group + build users (what a - # multi-user install does) before running it. - # - # Pin the Nix version: the latest (2.34) uses lazy-trees / a git - # cache that doesn't materialise flake-input `-source` paths into - # the store, so Stylix's import-from-derivation reads fail with - # "path '…-source' is not valid". 2.31.5 matches the Nix that wrote - # flake.lock locally and evaluates the flake cleanly. - run: | - NIX_VERSION=2.31.5 - groupadd -r nixbld 2>/dev/null || true - for i in $(seq 1 10); do - useradd -r -g nixbld -G nixbld -d /var/empty \ - -s /usr/sbin/nologin -c "Nix build user $i" "nixbld$i" 2>/dev/null || true - done - curl -L "https://releases.nixos.org/nix/nix-${NIX_VERSION}/install" | sh -s -- --no-daemon - echo "$HOME/.nix-profile/bin" >> "$GITHUB_PATH" - - - name: Materialize flake-input sources for eval - # Walker's flake reads its own source tree during evaluation: - # importTOML resources/config.toml in its home-manager module and - # lib.fileset.toSource for the package src (cargoLock.lockFile = - # "${src}/Cargo.lock"). On a cold store the input source isn't - # materialised, so flake check / eval die with "path '…-source' is - # not valid" (works locally only because prior builds materialised - # it; `nix flake archive` reports the input but doesn't make the - # fileset-derived src readable). `nix build` of walker's src - # eagerly realises it — and the input it reads from — priming the - # store so the later eval steps find every path valid. - run: | - nix build --no-link --impure --expr 'let f = builtins.getFlake (toString ./.); in f.inputs.walker.packages.${builtins.currentSystem}.default.src' - - - name: Check flake outputs (except the 22-variant pre-cache package) - # Stand-in for `nix flake check --no-build`. That also evaluates - # packages.allThemeVariants — a linkFarm whose drvPath forces all 22 - # home generations into memory at once, OOM-killing the runner. The - # eval matrix below already validates every palette one-at-a-time, so - # that package's CI value is near-zero. Here we force every other - # output (all 4 system closures incl. assertions, both standalone - # home generations, the installer VM package, the overlay, the app) - # and skip only allThemeVariants/default. Each tryEval is GC'd before - # the next, so peak memory is one config, not twenty-two. - run: | - nix eval --impure --raw --expr 'let - f = builtins.getFlake (toString ./.); - lib = f.inputs.nixpkgs.lib; - sys = "x86_64-linux"; - ok = x: (builtins.tryEval (builtins.seq x true)).success; - checks = { - "nixos.default" = ok f.nixosConfigurations.default.config.system.build.toplevel.drvPath; - "nixos.nomarchy-installer" = ok f.nixosConfigurations.nomarchy-installer.config.system.build.toplevel.drvPath; - "nixos.installerVm" = ok f.nixosConfigurations.installerVm.config.system.build.toplevel.drvPath; - "nixos.nomarchy-live" = ok f.nixosConfigurations.nomarchy-live.config.system.build.toplevel.drvPath; - "home.nomarchy" = ok f.homeConfigurations.nomarchy.activationPackage.drvPath; - "home.nixos" = ok f.homeConfigurations.nixos.activationPackage.drvPath; - "packages.installerVm" = ok f.packages.${sys}.installerVm.drvPath; - "overlays.default" = ok (builtins.isFunction f.overlays.default); - "apps.installerVm" = ok f.apps.${sys}.installerVm.program; - }; - failed = builtins.attrNames (lib.filterAttrs (_: v: !v) checks); - in if failed == [] then "ALL OK" - else throw ("flake-output checks failed: " + builtins.concatStringsSep ", " failed)' - - - name: Evaluate opt-in toggle / palette / home matrix - # flake check never flips a nomarchy.* toggle; this does. jq is - # provided via nix shell so the step doesn't depend on the runner - # image preinstalling it. - run: nix shell nixpkgs#jq --command ./bin/utils/nomarchy-eval-matrix - - - name: Lint nomarchy-* scripts (bash -n + shellcheck) - run: | - # Mirror what .githooks/pre-commit runs locally, but across the - # whole tree instead of just changed files. Pre-commit gates - # individual commits; CI gates branches (including --no-verify - # bypasses). - set -e - fail=0 - while IFS= read -r script; do - [[ -f "$script" ]] || continue - # Python helpers ship under the same nomarchy- prefix - # (e.g. nomarchy-haptic-touchpad). Skip non-bash. - head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue - if ! bash -n "$script"; then - echo "::error file=$script::bash syntax error" - fail=1 - fi - if ! nix shell nixpkgs#shellcheck --command shellcheck \ - --severity=error --shell=bash "$script"; then - echo "::error file=$script::shellcheck error-severity issue" - fail=1 - fi - done < <(find features/scripts/utils core/system/scripts \ - themes/engine/scripts \ - -maxdepth 1 -type f -name 'nomarchy-*') - exit "$fail" - - - name: docs/SCRIPTS.md is up to date - run: | - # Regenerate to a temp file and compare. If different, the - # contributor forgot to run the generator (or skipped the - # pre-commit hook). Fail loudly and tell them the fix. - ./bin/utils/nomarchy-docs-scripts --out /tmp/SCRIPTS.regen.md - if ! diff -q docs/SCRIPTS.md /tmp/SCRIPTS.regen.md >/dev/null; then - echo "::error::docs/SCRIPTS.md is stale." - echo "Run: ./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md" - echo "Then commit the regenerated file." - echo "--- diff ---" - diff -u docs/SCRIPTS.md /tmp/SCRIPTS.regen.md || true - exit 1 - fi - - - name: installer/hardware-db.sh references real nixos-hardware modules - run: | - # Every 4th-pipe-field in HARDWARE_DB is a nixos-hardware module - # name. Half the DB used to point at modules that don't exist - # (e.g. microsoft-surface-pro-8 — there's only -pro-intel and - # -pro-9), which made the install fail at eval time with - # cryptic "attribute not found" errors on real laptops. This - # step catches that regression class. - awk -F'|' '/^ "/ { gsub(/"/,"",$4); gsub(/^[[:space:]]+|[[:space:]]+$/,"",$4); if ($4) print $4 }' \ - installer/hardware-db.sh | sort -u > /tmp/db-refs.txt - nix eval --impure --json --expr ' - let - nh = (builtins.getFlake (toString ./.)).inputs.nixos-hardware.nixosModules; - in builtins.attrNames nh' \ - | nix shell nixpkgs#jq --command jq -r '.[]' | sort -u > /tmp/db-real.txt - missing=$(comm -23 /tmp/db-refs.txt /tmp/db-real.txt) - if [[ -n "$missing" ]]; then - echo "::error::hardware-db.sh references nixos-hardware modules that don't exist:" - printf ' - %s\n' $missing - echo "Either fix the name (check the actual attr in nixos-hardware) or drop the row." - exit 1 - fi diff --git a/.githooks/pre-commit b/.githooks/pre-commit deleted file mode 100755 index 1232c3c..0000000 --- a/.githooks/pre-commit +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -# Nomarchy pre-commit hook. -# -# Enable per-clone with: -# git config core.hooksPath .githooks -# -# Two responsibilities: -# 1. Lint changed nomarchy-* scripts (bash -n + shellcheck if available) -# so syntax errors and unquoted-var bugs don't ship. -# 2. Regenerate docs/SCRIPTS.md when any nomarchy-* script under the three -# script directories is added, modified, or deleted in this commit, and -# stage the refreshed file so it lands with the change. - -set -e - -repo_root="$(git rev-parse --show-toplevel)" -cd "$repo_root" - -script_dirs_re='^(features/scripts/utils|core/system/scripts|themes/engine/scripts)/nomarchy-' - -# 1. Lint changed scripts. bash -n catches syntax errors (always fatal). -# shellcheck catches unquoted-var, use-before-define, missing-shebang, etc. -# We only fail on severity=error so the long tail of pre-existing warnings -# (info / style / warning) doesn't block commits — those can be cleaned up -# incrementally without a flag day. -changed_scripts=$(git diff --cached --name-only --diff-filter=ACMR \ - | grep -E "$script_dirs_re" || true) -if [[ -n "$changed_scripts" ]]; then - while IFS= read -r script; do - [[ -f "$script" ]] || continue - # Only lint scripts with a bash shebang. nomarchy-* is a name - # convention, not a language guarantee — at least one Python helper - # ships under the same prefix (nomarchy-haptic-touchpad). - head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue - if ! bash -n "$script"; then - echo "pre-commit: bash syntax error in $script — aborting commit." >&2 - exit 1 - fi - if command -v shellcheck >/dev/null 2>&1; then - if ! shellcheck --severity=error --shell=bash "$script"; then - echo "pre-commit: shellcheck found error-level issues in $script — aborting commit." >&2 - echo "pre-commit: fix the reported issues, or rerun with --no-verify after a deliberate decision to ship." >&2 - exit 1 - fi - fi - done <<< "$changed_scripts" -fi - -# 2. Regenerate the script audit doc. -if git diff --cached --name-only --diff-filter=ACMRD | grep -qE "$script_dirs_re"; then - echo "pre-commit: regenerating docs/SCRIPTS.md (script change detected)…" - ./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md - git add docs/SCRIPTS.md -fi diff --git a/.gitignore b/.gitignore index 194c1b3..0b47b0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,10 @@ -# ---> Nix -# Ignore build outputs from performing a nix-build or `nix build` command +# Study material from the previous iteration — not part of the flake. +# Delete the directory once everything worth porting has been ported. +old_distro/ + +# Nix build artifacts result result-* -# Ignore automatically generated direnv output -.direnv - -# Local IDE settings -.claude/ - -# VM and ISO artifacts -*.qcow2 -*.qcow -*.iso +__pycache__/ +.DS_Store diff --git a/README.md b/README.md index e703f85..c1bff82 100644 --- a/README.md +++ b/README.md @@ -1,118 +1,240 @@ -# 👑 Nomarchy +# Nomarchy -**Nomarchy** is a professional-grade NixOS distribution that ships a highly curated Hyprland desktop on a strictly declarative, flake-based foundation. It provides a highly polished, "it just works" experience for power users who want a beautiful Wayland environment without sacrificing the reliability of NixOS. +**The rock-solid reproducibility of NixOS 26.05. The out-of-the-box polish of +Omarchy/Omakub.** One JSON file rules the look of the entire desktop; every +theme change is a Home Manager generation — atomic, rollbackable, never +partial. -## ✨ Key Features - -- **Purely Declarative:** Your entire desktop—themes, fonts, wallpapers, and toolchains—is defined in code. -- **Modular Merging Architecture:** Distro code and user code are strictly separated. You can update the distro core without ever touching or breaking your personal configurations. -- **Interactive "Smart" Installer:** Detects hardware, sets up networking, localizes timezones/keymaps, and offers multi-select software profiles. -- **Erase Your Darlings (Optional):** Optional BTRFS root-wipe on boot ensures your system stays pristine and 100% declarative. -- **Dynamic Theming Engine:** 20+ built-in themes with instant UI feedback via IPC (swww, waybar, and stylix). -- **Portable State (`nomarchy-sync`):** Easily backup and sync your declarative config and dynamic state (theme/wallpaper choices) to a private Git repo. - ---- - -## 📂 Component-Based Architecture - -Nomarchy uses a **Feature-Centric Directory Structure**. For a comprehensive breakdown of the system architecture, folder roles, and module logic, see the [Detailed Architecture Documentation](docs/STRUCTURE.md). - -Configuration, modules, and utilities are strictly organized to maintain sanity as the system grows. - -- **`core/`**: Foundational OS & User defaults (Bootloader, Audio, Bluetooth, core system features). -- **`features/`**: Isolated modules containing Nix logic and raw dotfiles. - - **`features/apps/`**: App-specific configs (e.g., `features/apps/btop/`, `features/apps/kitty/`), each containing their own `default.nix` and standalone `config/` directory mapped via Home Manager. - - **`features/desktop/`**: Desktop environment components (e.g., Hyprland, Waybar). - - **`features/scripts/utils/`**: Consolidated repository for all custom Nomarchy bash scripts, centrally packaged and injected into the user's `PATH` with correct dependencies. -- **`themes/`**: The global theming engine. It holds pure color data and logic. *Theme-specific app layouts* (like a custom Waybar layout) are stored directly inside the app's feature folder, solving the matrix problem of theming. - ---- - -## 📥 Installation - -### 1. Try it in a VM (Recommended) -Verify the experience without touching your hardware: -```bash -./bin/utils/nomarchy-test-installer ``` -This builds a full graphical VM of the installer environment. Once inside, click the **Install Nomarchy** icon or run `nomarchy-install`. - -### 2. Build the Installer ISO -To install on physical hardware, generate your own bootable image: -```bash -./features/scripts/utils/nomarchy-build-iso # Minimal TTY installer -./features/scripts/utils/nomarchy-build-live-iso # Graphical try-before-install +┌──────────────────────────────────────────────────────────────────────────┐ +│ theme-state.json (single source of truth) │ +│ lives INSIDE your flake checkout, git-tracked │ +└───────────────────────────────────┬──────────────────────────────────────┘ + │ + nomarchy-theme-sync apply gruvbox + 1. merges the preset into the JSON (atomic write) + 2. runs `home-manager switch` (no sudo, no system rebuild) + │ + ▼ pure read (nomarchy.stateFile) +┌──────────────────────────────────────────────────────────────────────────┐ +│ Home Manager bakes EVERYTHING into one read-only generation: │ +│ Hyprland (colors/gaps/borders) Waybar (palette or whole-swap) │ +│ Ghostty (full ANSI palette) btop (asset or generated) │ +│ Stylix → GTK, Qt, cursors, fonts │ +└───────────────────────────────────┬──────────────────────────────────────┘ + ▼ + wallpaper via awww (the one runtime piece: + applied post-switch + at session start, `bg next` cycles) ``` -The ISO will be located at `./result/iso/nixos-*.iso`. Flash it to a USB drive and boot. -### 3. Run the Installer -Once booted into the Live environment, launch the installer: -```bash -nomarchy-install +## 1. Layout + +Flat on purpose. Two module trees, one options file each, no hidden layers. + +``` +. +├── flake.nix # inputs + the downstream API (exports below) +├── lib.nix # nomarchy.lib.mkFlake — one-call downstream wrapper +├── theme-state.json # ★ THE single source of truth (git-tracked!) +├── themes/ # 21 presets: .json + optional / assets +│ ├── nord.json # palette (required, works alone) +│ └── nord/ # assets (optional, fixed filenames) +│ ├── backgrounds/ # wallpapers (auto-picked, SUPER+SHIFT+T cycles) +│ ├── btop.theme # hand-made config drop (else generated) +│ └── waybar.css # whole-swap: replaces the generated bar style +├── modules/ +│ ├── nixos/ # the distro, system side +│ │ ├── default.nix # Hyprland session, Pipewire, greetd, fonts +│ │ └── options.nix # nomarchy.system.* toggles +│ └── home/ # the distro, user side +│ ├── default.nix # entry point +│ ├── options.nix # nomarchy.* option surface +│ ├── theme.nix # JSON ingestion + wallpaper hook +│ ├── stylix.nix # GTK/Qt/cursors/fonts from the same JSON +│ ├── hyprland.nix # all JSON-driven +│ ├── waybar.nix +│ ├── ghostty.nix +│ └── btop.nix +├── hosts/ +│ ├── default/ # reference machine (thin: boot, user, hostname) +│ └── live.nix # bootable live ISO (try the distro, no install) +├── pkgs/ +│ ├── nomarchy-theme-sync/ # state writer + rebuild dispatcher (Python) +│ └── nomarchy-install/ # live-ISO installer (gum + disko + mkFlake) +├── templates/downstream/ # `nix flake init -t` starter for users +├── docs/TESTING.md # how to verify changes (incl. AI-agent rules) +└── tools/ # maintainer-only + ├── import-palettes.py # converts old-distro themes → JSON + assets + ├── test-live-iso.sh # build the ISO + boot it in QEMU + ├── test-install.sh # full offline-install regression in QEMU + └── vm/ # headless-VM helpers (QMP keys, VNC shots, + # offline-pin gap analysis) ``` -The wizard will guide you through: -- **Networking:** An interactive wizard to connect to Wi-Fi if needed. -- **Hardware:** Optimized profiles for Dell XPS, Framework, Apple T2, and more. -- **Storage:** Choice between Standard Ext4 or Encrypted BTRFS with optional **Impermanence**. -- **Localization:** Searchable timezones and keyboard layout selection. -### Already on NixOS? -Layer Nomarchy onto an existing 25.11 install without reformatting — see the [Migration Guide](docs/MIGRATION.md). +**Rule of thumb:** `modules/` is the distro (reusable, no machine specifics), +`hosts/` is a machine, `themes/` is data, `pkgs/` is code, `tools/` is +maintainer-only. If a new file doesn't obviously belong to one of those, it +probably shouldn't exist. ---- +## 2. Try it first (live ISO) -## 🛠️ Configuration & Usage +Boot the full desktop from a USB stick or VM without installing anything: -Nomarchy uses a "Downstream" model. After installation, your configuration lives in `/etc/nixos/` and is split into three main parts: +```sh +nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage +# → result/iso/*.iso — dd to a stick, or boot it in QEMU: +tools/test-live-iso.sh +``` -### 1. Distro Core (Upstream) -Managed via the public Git repository. This is the engine. You should generally not modify files here. +The live session auto-logs-in, seeds the flake at `~/.nomarchy`, and pins +the locked inputs into the ISO store — so theme switching (including the +`home-manager switch` it triggers) works **offline**, exactly like on an +installed system. Verification checklist: [docs/TESTING.md](docs/TESTING.md). + +Like what you see? **`nomarchy-install`** (in a terminal) walks you through +installing to disk: pick a disk, LUKS2 full-disk encryption **by default** +(in exchange the desktop logs in passwordless — the passphrase already +gates the machine), user + hostname + timezone, hardware autodetection +(DMI → nixos-hardware profile), a hibernation-ready swapfile sized to RAM, +then disko partitions (GPT + ESP + BTRFS subvolumes incl. `@snapshots` — +snapper timeline snapshots are on) and `nixos-install` runs — **without a +network** when the ISO was built from a clean tree (the target's +`flake.lock` is composed from the rev the ISO carries). The installed +machine gets the standard downstream layout: the flake at `~/.nomarchy` +(`/etc/nixos` symlinks to it), one `mkFlake` call, your +`system.nix`/`home.nix`. First boot lands in the fully themed desktop — +the installer pre-activates the Home Manager generation. UEFI only for now. + +## 3. Using Nomarchy on your machine (downstream) + +Nomarchy is consumed as a flake input — you never fork or edit this repo: + +```sh +mkdir my-machine && cd my-machine +nix flake init -t "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1" +``` + +You own two files day-to-day: `system.nix` and `home.nix` (plus +`theme-state.json`, written by the CLI). Your `flake.nix` is set up once — +later by the installer — and never hand-edited; it's a single call: -### 2. Your System (`system.nix`) -Add system-wide packages, services, and hardware tweaks here. -*Example: Adding a persistent directory for Docker if using Impermanence:* ```nix -environment.persistence."/persist" = { - directories = [ "/var/lib/docker" ]; -}; +outputs = { nomarchy, ... }: + nomarchy.lib.mkFlake { + src = ./.; + username = "me"; + hardwareProfile = "framework-13-7040-amd"; # optional, nixos-hardware name + }; ``` -### 3. Your User Environment (`home.nix`) -Add user-level packages, aliases, and dotfiles here. -*Example: Overriding the default terminal:* -```nix -nomarchy.home.terminal = "kitty"; +| `mkFlake` arg | Default | Purpose | +|---|---|---| +| `src` | — (required) | Your flake directory (`./.`) | +| `username` | — (required) | Login name; flows into `system.nix` and names the HM config | +| `hardwareProfile` | `null` | One [nixos-hardware](https://github.com/NixOS/nixos-hardware) module name, or a list of them (pinned + tested by Nomarchy; unknown names fail with suggestions) | +| `system` | `"x86_64-linux"` | Platform | + +(Power users can skip `mkFlake` and wire `nixosModules.nomarchy` / +`homeModules.nomarchy` / `overlays.default` by hand — the wrapper is sugar.) + +Two deliberately separate rebuild paths: + +```sh +sudo nixos-rebuild switch --flake .#default # system: rare +home-manager switch --flake .#me # desktop: every theme change, no sudo ``` -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). +Override anything with plain NixOS/HM options (the distro uses `mkDefault` +throughout) or the `nomarchy.*` surface: -### 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: +| Option | Default | Purpose | +|---|---|---| +| `nomarchy.stateFile` | — (required) | Path to your theme-state.json | +| `nomarchy.terminal` | `"ghostty"` | Terminal for keybinds and `$TERMINAL` | +| `nomarchy.hyprland.enable` | `true` | Nomarchy's Hyprland config | +| `nomarchy.waybar.enable` | `true` | Nomarchy's Waybar | +| `nomarchy.ghostty.enable` | `true` | Nomarchy's Ghostty | +| `nomarchy.btop.enable` | `true` | btop with per-theme colors | +| `nomarchy.stylix.enable` | `true` | GTK/Qt/cursor theming | +| `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed | +| `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet | +| `nomarchy.system.audio.enable` | `true` | Pipewire stack | +| `nomarchy.system.bluetooth.enable` | `true` | Bluetooth + blueman | -```bash -sys-update # Rebuilds the NixOS system (Runs: sudo nixos-rebuild switch --flake .#default --impure) -env-update # Reloads your Home Manager environment (Runs: home-manager switch --flake .#default --impure) +## 4. How theming works + +### Pure JSON ingestion + +The trap with "read a mutable file from Nix" is pure evaluation: flakes +cannot read arbitrary `$HOME` paths without `--impure` (the old prototype +required it — never again). Nomarchy's convention: **the state file lives +inside the consuming flake** and is wired via +`nomarchy.stateFile = ./theme-state.json;`. Reading it is pure — it's flake +source. It must be git-tracked (`nomarchy-theme-sync` runs +`git add --intent-to-add` after every write as a safety net). + +### One change = one generation + +`nomarchy-theme-sync apply ` merges the preset into the JSON and runs +`home-manager switch` (override the command with `$NOMARCHY_REBUILD`, or pass +`--no-switch` to only write). Everything is baked: Hyprland, Waybar, Ghostty, +btop, and — via Stylix, mapped onto base16 roles — GTK, Qt, cursors and +fonts. No runtime patching means no partial states, and `home-manager +generations` is also your theme history. Waybar even restyles in place: it +re-reads `style.css` when the symlink flips. + +The **wallpaper** is the one runtime piece (awww — nixpkgs' swww — is +imperative; nothing in Nix consumes the path): applied at session start and +after every switch via a tiny activation hook, cycled instantly with +`bg next`. + +### Per-theme app assets (`themes//`) + +Recoloring covers 95% of theming; the rest is one optional assets directory +per theme — a single place to look, unlike the old distro's split: + +| Asset | Mechanism | +|---|---| +| `backgrounds/` | wallpapers; empty `wallpaper` in the state means "first one"; `bg next` cycles | +| `btop.theme` | baked into the generation (generated from the palette when absent) | +| `waybar.css` | **whole-swap**: replaces the generated bar style entirely (probed at eval time, self-contained) | +| `waybar.jsonc` | whole-swap for the bar *layout* (must be plain JSON) | + +Six ported themes ship a `waybar.css` identity (catppuccin, lumon, nord, +retro-82, summer-day, summer-night). Custom user themes can live in +`$NOMARCHY_PATH/themes/` (preset lookup) and `nomarchy.themesDir` (eval-time +asset probe). + +## 5. Day-to-day + +```sh +nomarchy-theme-sync list # 21 presets (nord, gruvbox, rose-pine, …) +nomarchy-theme-sync apply kanagawa # whole desktop, one generation (~a switch) +nomarchy-theme-sync set ui.gapsOut 16 # tweak one knob (also a switch) +nomarchy-theme-sync bg next # cycle wallpapers — instant, no rebuild +nomarchy-theme-sync bg auto # back to the theme's default wallpaper +nomarchy-theme-sync get colors.accent ``` ---- +Keybinds: `SUPER+Return` terminal · `SUPER+D` launcher · `SUPER+T` theme +picker · `SUPER+SHIFT+T` next wallpaper · `SUPER+Q` close · `SUPER+1..9` +workspaces · `Print` region screenshot. -## 🚀 Commands & Keybindings +## 6. Extending -The full list lives in [`docs/KEYBINDINGS.md`](docs/KEYBINDINGS.md) (auto-generated from the Hyprland configs). A few highlights: +- **New theme:** drop a JSON into `themes/` (schema = any existing preset), + plus an optional `themes//` assets directory. +- **New themed value:** add the key to `theme-state.json` and consume it in + the Nix modules. One place — there is no second renderer to keep in sync. +- **Importing more old-distro palettes:** + `tools/import-palettes.py themes/`. -| Keybinding | Action | -| :--- | :--- | -| `Super + Space` | **App Launcher** (Walker) | -| `Super + Shift + Space` | **Nomarchy Menu** (Walker) | -| `Super + Alt + Space` | **Toggle Top Bar** (Waybar) | -| `Super + Return` | Open Terminal | -| `Super + Q` | Close Window | +## Roadmap -### Utility Scripts -Nomarchy includes dozens of productivity scripts available in your PATH. Some highlights: -- `nomarchy-sync push `: Backup your setup to Git. -- `nomarchy-theme-bg-next`: Cycle to the next wallpaper in the current theme. -- `nomarchy-menu`: The central hub for all utilities and pickers. - ---- -*Built with ❤️ using NixOS, Hyprland, Stylix, and the Nomarchy Community.* +- **Faster switches:** move `backgrounds/` out of the flake source (the 86 MB + re-copy on every state write is the main eval tax), then pre-built theme + variants if still needed +- Plymouth + SDDM/greeter theming from the same JSON +- Installer round 2: multi-disk BTRFS RAID, impermanence, BIOS/legacy + boot (v1 `nomarchy-install` is single-disk UEFI — see `pkgs/nomarchy-install`) +- `hyprlock`/`hypridle`, swayosd, launch-or-focus UX scripts diff --git a/bin/utils/nomarchy-docs-keybindings b/bin/utils/nomarchy-docs-keybindings deleted file mode 100755 index d6eb1d6..0000000 --- a/bin/utils/nomarchy-docs-keybindings +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# nomarchy-docs-keybindings -# -# Regenerates docs/KEYBINDINGS.md from the Hyprland binding files. Run from the -# repo root or anywhere — paths are resolved relative to this script. -# -# nomarchy-docs-keybindings # write to stdout -# nomarchy-docs-keybindings --out docs/KEYBINDINGS.md -# -# Source files in render order. Each entry is "|". - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" - -sources=( - "core/home/config/nomarchy/default/hypr/bindings/utilities.conf|Utilities" - "core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf|Tiling" - "core/home/config/nomarchy/default/hypr/bindings/clipboard.conf|Clipboard" - "core/home/config/nomarchy/default/hypr/bindings/media.conf|Media keys" - "features/desktop/hyprland/config/bindings.conf|Apps & web shortcuts" -) - -prettify_key() { - case "$1" in - code:10) echo "1" ;; code:11) echo "2" ;; code:12) echo "3" ;; - code:13) echo "4" ;; code:14) echo "5" ;; code:15) echo "6" ;; - code:16) echo "7" ;; code:17) echo "8" ;; code:18) echo "9" ;; - code:19) echo "0" ;; - XF86AudioRaiseVolume) echo "Volume Up" ;; - XF86AudioLowerVolume) echo "Volume Down" ;; - XF86AudioMute) echo "Mute" ;; - XF86AudioMicMute) echo "Mic Mute" ;; - XF86AudioPlay) echo "Play/Pause" ;; - XF86AudioStop) echo "Stop" ;; - XF86AudioNext) echo "Next Track" ;; - XF86AudioPrev) echo "Previous Track" ;; - XF86MonBrightnessUp) echo "Brightness Up" ;; - XF86MonBrightnessDown) echo "Brightness Down" ;; - XF86KbdBrightnessUp) echo "Kbd Brightness Up" ;; - XF86KbdBrightnessDown) echo "Kbd Brightness Down" ;; - XF86KbdLightOnOff) echo "Kbd Backlight" ;; - *) echo "$1" ;; - esac -} - -trim() { sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//'; } - -render_section() { - local file="$1" title="$2" - [[ ! -f "$repo_root/$file" ]] && return - local rows - rows=$(grep -E '^[[:space:]]*bind[a-z]*[[:space:]]*=' "$repo_root/$file" || true) - [[ -z "$rows" ]] && return - - local body="" - while IFS= read -r line; do - # Strip the "bindXXX =" prefix. - local rhs="${line#*=}" - local mods key desc - IFS=',' read -r mods key desc _ <<<"$rhs" - mods=$(printf '%s' "${mods:-}" | trim) - key=$(printf '%s' "${key:-}" | trim) - desc=$(printf '%s' "${desc:-}" | trim) - [[ -z "$desc" ]] && continue # skip non-descriptive bindings - [[ -z "$mods" ]] && mods="—" - key=$(prettify_key "$key") - body+=$(printf '| %s | %s | %s |\n' "$mods" "$key" "$desc") - body+=$'\n' - done <<<"$rows" - - [[ -z "$body" ]] && return - - printf '\n## %s\n\n' "$title" - printf '_Source: `%s`_\n\n' "$file" - printf '| Modifiers | Key | Action |\n' - printf '| --- | --- | --- |\n' - printf '%s' "$body" -} - -main() { - cat <<'HEADER' -# Nomarchy Keybindings - -Auto-generated from the Hyprland binding files. **Do not edit by hand.** -Re-run the generator after changing any `bindings/*.conf`: - -```bash -./bin/utils/nomarchy-docs-keybindings --out docs/KEYBINDINGS.md -``` - -`SUPER` is the Meta / Win key. `code:NN` keys (X11 digit keycodes) are -shown as the digit they correspond to. Media keys (`XF86Audio*`, -`XF86MonBrightness*`, …) are prettified. -HEADER - for entry in "${sources[@]}"; do - render_section "${entry%|*}" "${entry#*|}" - done -} - -out="" -if [[ "${1:-}" == "--out" ]]; then - out="${2:?--out needs a path}"; shift 2 -fi -if [[ -n "$out" ]]; then - main >"$out" -else - main -fi diff --git a/bin/utils/nomarchy-docs-scripts b/bin/utils/nomarchy-docs-scripts deleted file mode 100755 index daef509..0000000 --- a/bin/utils/nomarchy-docs-scripts +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env bash -# Generator tolerates "no matches" exit codes from grep | sort. -# pipefail and -e off; -u stays. -set -u - -# Deterministic collation regardless of the caller's locale. Without this -# `sort` orders rows and caller lists by the ambient LC_COLLATE (UTF-8 on a -# dev box, C in the CI container), so the committed doc and CI's regenerated -# copy disagree on ordering and the drift check fails on pure noise. -export LC_ALL=C - -# nomarchy-docs-scripts -# -# Regenerates docs/SCRIPTS.md from the repo state. Produces: -# 1. Header + status legend + regen instructions. -# 2. Table of every nomarchy-* script (location, callers, status). -# 3. Table of every menu entry in features/scripts/utils/nomarchy-menu -# (submenu, label, target command, status). -# 4. Snapshot list of orphaned references (called somewhere, no script). -# -# Status heuristic in Phase A: -# kept — file exists AND is called from at least one *.nix / *.conf / -# shell file outside its own directory. -# unused? — file exists but no caller found. Phase B decides delete-dead -# vs intentional public API. -# missing — referenced but no file. Phase B decides port-from-omarchy -# vs delete-dead vs stub-with-notify. -# -# nomarchy-docs-scripts # write to stdout -# nomarchy-docs-scripts --out docs/SCRIPTS.md - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd "$repo_root" - -# --- Inventory ------------------------------------------------------------- - -# Where scripts live, in render order. -declare -A loc_label=( - ["features/scripts/utils"]="features/scripts/utils" - ["core/system/scripts"]="core/system/scripts" - ["themes/engine/scripts"]="themes/engine/scripts" -) -script_dirs=(features/scripts/utils core/system/scripts themes/engine/scripts) - -# Build name → location map (associative array of basename → repo-relative dir). -declare -A script_loc -for dir in "${script_dirs[@]}"; do - [[ -d "$dir" ]] || continue - while IFS= read -r f; do - script_loc["$(basename "$f")"]="$dir" - done < <(find "$dir" -maxdepth 1 -type f -name 'nomarchy-*') -done - -# Find every nomarchy-* token referenced anywhere outside the script dirs. -# (We exclude the script files themselves so they don't list themselves as -# their own caller.) -# File types we search for references. *.md catches docs and README; -# branding/hook/extension files have varied or no extensions. -# *.lua catches elephant providers; *.ini catches mako on-button-* hooks; -# *.desktop catches MimeType-registered URL handlers. -grep_includes=( - --include='*.nix' --include='*.conf' --include='*.sh' --include='*.md' - --include='nomarchy-*' --include='*.jsonc' --include='*.json' - --include='*.toml' --include='*.ini' --include='*.lua' - --include='*.desktop' --include='*.txt' --include='*.sample' -) -search_dirs=(core features themes installer hosts bin lib README.md) - -# Files whose mentions of nomarchy-* are documentation about the scripts, -# not real callers. Excluded from caller discovery so they don't promote -# every script to `kept`. -self_refs=(docs/SCRIPTS.md docs/ROADMAP.md docs/AGENT.md) - -ref_files_per_cmd() { - local cmd="$1" - local self_pattern - self_pattern=$(IFS='|'; echo "${self_refs[*]}") - grep -rlE "\\b${cmd}\\b" \ - "${grep_includes[@]}" \ - "${search_dirs[@]}" 2>/dev/null \ - | grep -vE "^(features/scripts/utils|core/system/scripts|themes/engine/scripts)/${cmd}$" \ - | grep -vE "^(${self_pattern})$" \ - | sort -u -} - -# All distinct nomarchy-* tokens we see anywhere in the repo. -# Final char must be alphanumeric — dropping trailing-dash matches like -# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`). -# Restrict to grep_includes so binaries / tmpfiles don't pollute the set. -# The first `grep -vE` drops lines where `nomarchy-*` is a derivation / -# tmp file / sudoers basename / systemd unit / flake output / docker -# container identifier rather than a shell invocation. -# The second `grep -vE` is a token-level safety net for prefix-only -# tokens left over from wildcards/expansions (e.g. `nomarchy-pkg-*`). -all_refs=$(grep -rhE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \ - "${grep_includes[@]}" \ - "${search_dirs[@]}" 2>/dev/null \ - | grep -vE \ - -e '(pname|name)[[:space:]]*=[[:space:]]*"nomarchy-' \ - -e '/tmp/nomarchy-' \ - -e '/etc/sudoers\.d/[^"[:space:]]*nomarchy-' \ - -e 'nixosConfigurations\.nomarchy-' \ - -e 'packages\.[^.]+\.nomarchy-' \ - -e '\./result/bin/run-nomarchy-' \ - -e 'mktemp[[:space:]]+[^|]*-t[[:space:]]+nomarchy-' \ - -e '(TIMER_NAME|NOPASSWD_FILE|UNIT_NAME)=.*nomarchy-' \ - -e '\.services\.nomarchy-' \ - -e 'docker[[:space:]]+[^|]*nomarchy-' \ - | grep -oE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \ - | grep -vE '^(nomarchy-launch|nomarchy-brightness|nomarchy-cmd|nomarchy-pkg|nomarchy-restart|nomarchy-toggle|nomarchy-theme|nomarchy-webapp-handler|nomarchy-font-selector|nomarchy-theme-selector|nomarchy-wallpaper-selector|nomarchy-setup|nomarchy-refresh|nomarchy-scripts|nomarchy-system-scripts|nomarchy-theme-engine-scripts)$' \ - | grep -vE '^(nomarchy-plymouth|nomarchy-sddm-theme|nomarchy-live|nomarchy-rev|nomarchy-windows)$' \ - | grep -vE '^(nomarchy-eval-matrix|nomarchy-docs-scripts|nomarchy-docs-keybindings)$' \ - | sort -u) -# The second denylist covers identifiers whose ambiguity survives the line -# filter: `nomarchy-plymouth` / `nomarchy-sddm-theme` are Nix derivation -# names referenced as bare idents in `[...]` lists, `nomarchy-live` is an -# ISO label that shows up in comments, `nomarchy-rev` is `/etc/nomarchy-rev` -# (written by the ISO), and `nomarchy-windows` is a docker container name -# in compose heredocs. -# The third denylist: `nomarchy-eval-matrix`, `nomarchy-docs-scripts` (this -# generator), and `nomarchy-docs-keybindings` are bin/utils repo tools that -# name themselves in their own headers / AGENT.md — not user-facing scripts -# that ship in nomarchy-system-scripts. - -# --- Render: header -------------------------------------------------------- - -main() { - cat <<'HEADER' -# Nomarchy Script & Menu Audit - -Auto-generated table for [Pillar 3 of the roadmap](ROADMAP.md#3-pillar-script--menu-audit). -**Do not edit by hand.** Regenerate after script or menu changes: - -```bash -./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md -``` - -The status column uses a Phase A heuristic — `kept` / `unused?` / `missing`. -Phase B (per-batch PRs) refines those into `port-from-omarchy`, -`delete-dead`, or `stub-with-notify` and updates the rows. - -## Status legend - -- `kept` — script exists and is called from somewhere outside its own directory. -- `unused?` — script exists but no caller was found. Could be dead, could be - intentional public API. Phase B triage decides. -- `missing` — referenced from code but no script file exists. Phase B triage - decides whether to port from Omarchy upstream, delete the caller, or stub - with `notify-send`. -- `port-from-omarchy` — Phase B verdict: lift the upstream Omarchy script, - rewrite for NixOS paths. -- `delete-dead` — Phase B verdict: remove and update callers. -- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub. - -HEADER - - # --- Render: scripts table ---------------------------------------------- - printf '## Scripts (%d)\n\n' "${#script_loc[@]}" - printf '| Script | Location | Callers | Status | Notes |\n' - printf '| --- | --- | --- | --- | --- |\n' - - # Sort scripts by name. - for name in $(printf '%s\n' "${!script_loc[@]}" | sort); do - local dir="${script_loc[$name]}" - local callers status callers_str - callers=$(ref_files_per_cmd "$name") - if [[ -z "$callers" ]]; then - status='`unused?`' - callers_str='—' - else - status='`kept`' - # Trim caller list to 2 entries + count. - local count - count=$(printf '%s\n' "$callers" | wc -l) - if (( count > 2 )); then - callers_str=$(printf '%s\n' "$callers" | head -2 | paste -sd, -) - callers_str="$callers_str, +$((count - 2)) more" - else - callers_str=$(printf '%s\n' "$callers" | paste -sd, -) - fi - fi - printf '| `%s` | `%s` | %s | %s | |\n' \ - "$name" "$dir" "$callers_str" "$status" - done - echo - - # --- Render: missing references ----------------------------------------- - printf '## Missing references\n\n' - printf 'Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bin/`, `lib/` that have no matching script file.\n\n' - printf '| Token | Referenced in | Status |\n' - printf '| --- | --- | --- |\n' - while IFS= read -r token; do - [[ -z "$token" ]] && continue - [[ -n "${script_loc[$token]:-}" ]] && continue - local refs - self_pattern=$(IFS='|'; echo "${self_refs[*]}") - refs=$(grep -rlE "\\b${token}\\b" \ - "${grep_includes[@]}" \ - "${search_dirs[@]}" 2>/dev/null \ - | grep -vE "^(${self_pattern})$" \ - | sort -u) - [[ -z "$refs" ]] && continue - local count refs_str - count=$(printf '%s\n' "$refs" | wc -l) - if (( count > 2 )); then - refs_str=$(printf '%s\n' "$refs" | head -2 | paste -sd, -) - refs_str="$refs_str, +$((count - 2)) more" - else - refs_str=$(printf '%s\n' "$refs" | paste -sd, -) - fi - printf '| `%s` | %s | `missing` |\n' "$token" "$refs_str" - done <<<"$all_refs" - echo - - # --- Render: menu items ------------------------------------------------- - printf '## Menu items\n\n' - printf 'Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*_menu` function becomes one row.\n\n' - printf '| Submenu | Entry | Calls | Status |\n' - printf '| --- | --- | --- | --- |\n' - - awk ' - /^show_[a-z_]+_menu\(\) {/ { sub(/\(\) {/, ""); current=$1; in_func=1; next } - /^[a-z_]+\(\) {/ && !/^show_/ { current=""; in_func=0; next } - /^}$/ { current=""; in_func=0; next } - !in_func { next } - /^ case \$\(menu / { - # extract the menu title between the first pair of double quotes - match($0, /menu "[^"]+" "[^"]+"/); - if (RSTART == 0) next; - title=substr($0, RSTART, RLENGTH); - # second quoted string is the option list - n=split(title, parts, "\""); - title=parts[2]; - options=parts[4]; - # split options on \n - split(options, opts, "\\\\n"); - pending_submenu=current; - pending_title=title; - for (i=1;i<=length(opts);i++) pending_opts[i]=opts[i]; - pending_count=length(opts); - next - } - /^ \*[A-Za-z]/ { - # case arm — extract pattern between the first * and the closing ) - match($0, /\*[^)]*\)/); - if (RSTART == 0) next; - arm=substr($0, RSTART, RLENGTH); - gsub(/[*)]/, "", arm); - gsub(/^[[:space:]]+|[[:space:]]+$/, "", arm); - # action follows the ) - rest=substr($0, RSTART+RLENGTH); - sub(/^[[:space:]]+/, "", rest); - sub(/[[:space:]]*;;[[:space:]]*$/, "", rest); - # match the first nomarchy-* token in the action - cmd="" - if (match(rest, /nomarchy-[a-z0-9-]+/)) { - cmd=substr(rest, RSTART, RLENGTH); - } - printf "%s|%s|%s\n", pending_submenu, arm, cmd; - } - ' features/scripts/utils/nomarchy-menu > /tmp/nomarchy-menu-rows.$$ - - while IFS='|' read -r submenu entry cmd; do - [[ -z "$entry" ]] && continue - [[ "$entry" =~ ^\) ]] && continue - status='`kept`' - if [[ -n "$cmd" ]]; then - if [[ -z "${script_loc[$cmd]:-}" ]]; then - status='`missing`' - fi - else - cmd='_(inline)_' - fi - printf '| `%s` | %s | `%s` | %s |\n' "$submenu" "$entry" "$cmd" "$status" - done < /tmp/nomarchy-menu-rows.$$ - rm -f /tmp/nomarchy-menu-rows.$$ - echo -} - -out="" -if [[ "${1:-}" == "--out" ]]; then - out="${2:?--out needs a path}"; shift 2 -fi -if [[ -n "$out" ]]; then - main >"$out" -else - main -fi diff --git a/bin/utils/nomarchy-eval-matrix b/bin/utils/nomarchy-eval-matrix deleted file mode 100755 index 84d276c..0000000 --- a/bin/utils/nomarchy-eval-matrix +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env bash -# nomarchy-eval-matrix -# -# Evaluates the opt-in nomarchy.* surface that the per-output flake check -# never touches. flake check only evaluates the default configs, so a bug -# that only fires when a toggle is flipped — a renamed option, a failed -# assertion, a stale reference — sails straight through. Two such bugs -# shipped to main before this existed (the vscode option rename and the -# impermanence systemd-stage-1 assertion); both would have been caught -# here. -# -# Cost matters: this runs in CI on a small self-hosted runner, and the -# first cut (one full `extendModules` eval per individual toggle, plus one -# per palette) did ~39 whole-system evaluations and ran ~3h. So: -# -# * Compatible toggles are COMBINED into a few configs (a failure means -# re-run the offending toggle alone to pinpoint it — see the trace hint -# at the end). laptop vs desktop formFactor conflict, so they split; -# impermanence's multi-disk variant gets its own config for the alt -# mainLuksName. -# * Palettes are checked DIRECTLY via the lib — the per-palette risk is -# the system-side Plymouth base00 → RGB math (fromHexString of 2-char -# slices in themes/engine/plymouth.nix), which is pure and needs no -# module-system instantiation. Forcing the whole palette attrset also -# catches a missing/garbled palette. -# * The standalone homeConfigurations aren't re-checked here — the -# per-output flake-check step already forces both. -# -# Net: a handful of full evals instead of ~39. Results come from -# builtins.tryEval so one failure doesn't mask the rest. -# -# nomarchy-eval-matrix # run the matrix, table + exit code -# -# Add a new opt-in option's coverage by dropping it into the relevant -# combined scenario in the `scenarios` attrset below. - -set -u - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd "$repo_root" - -NIX=(nix --extra-experimental-features 'nix-command flakes') - -read -r -d '' EXPR <<'NIXEOF' || true -let - f = builtins.getFlake (toString ./.); - lib = f.inputs.nixpkgs.lib; - nl = import ./lib { inherit lib; }; - default = f.nixosConfigurations.default; - - forced = x: (builtins.tryEval (builtins.seq x true)).success; - - # Combined opt-in scenarios. Each enables as many compatible toggles as - # possible so CI does ~3 full-system evals, not one per toggle. Home-side - # options go under home-manager.users.nomarchy.nomarchy.*. - scenarios = { - # laptop formFactor + every compatible system/home toggle + impermanence. - "laptop-stack" = { - nomarchy.system = { - formFactor = "laptop"; - laptop.enable = true; - accessibility.enable = true; - gaming.enable = true; - features.hybridGPU = true; - hibernation.enable = true; - virtualization.docker.enable = true; - impermanence.enable = true; - }; - nomarchy.hardware.fwupd = true; - home-manager.users.nomarchy.nomarchy = { - accessibility.enable = true; - gaming.enable = true; - overrides.enable = true; - panelPosition = "bottom"; - keymap = { layout = "de"; variant = "nodeadkeys"; }; - toggles = { waybar = false; idle = false; nightlight = false; }; - }; - }; - - # desktop preset — conflicts with the laptop formFactor, so its own eval. - "desktop-stack" = { - nomarchy.system = { formFactor = "desktop"; desktop.enable = true; }; - }; - - # impermanence multi-disk variant (alternate mainLuksName). - "impermanence-multi" = { - nomarchy.system.impermanence = { enable = true; mainLuksName = "crypted_main"; }; - }; - }; - - evalToplevel = mod: - forced (default.extendModules { modules = [ mod ]; }).config.system.build.toplevel.drvPath; - - # base00 → the three byte values the Plymouth template substitutes; plus - # the whole palette attrset, so a missing/garbled palette is caught. - paletteOk = name: - let - p = nl.getPalette name; - b = p.base00; - bytes = [ - (lib.fromHexString (lib.substring 0 2 b)) - (lib.fromHexString (lib.substring 2 2 b)) - (lib.fromHexString (lib.substring 4 2 b)) - ]; - in forced (builtins.deepSeq [ p bytes ] true); -in { - scenarios = builtins.mapAttrs (_: evalToplevel) scenarios; - palettes = builtins.listToAttrs (map (n: { name = n; value = paletteOk n; }) nl.themeNames); -} -NIXEOF - -echo "Evaluating combined toggle scenarios + per-palette colour math..." -json="$("${NIX[@]}" eval --impure --json --expr "$EXPR" 2>/tmp/eval-matrix.err)" -status=$? -if [[ $status -ne 0 || -z "$json" ]]; then - echo "ERROR: the matrix evaluation aborted before producing results." >&2 - echo "This usually means an uncatchable error (abort/infinite recursion) in one" >&2 - echo "scenario, or a syntax error in the expression. Full output:" >&2 - cat /tmp/eval-matrix.err >&2 - exit 1 -fi - -# Render each section and tally failures. -fails=0 -render() { - local section="$1" - echo - echo "=== $section ===" - while IFS=$'\t' read -r name ok; do - if [[ "$ok" == "true" ]]; then - printf ' ok %s\n' "$name" - else - printf ' FAIL %s\n' "$name" - fails=$((fails + 1)) - fi - done < <(echo "$json" | jq -r --arg s "$section" '.[$s] | to_entries[] | "\(.key)\t\(.value)"' | sort) -} - -render scenarios -render palettes - -echo -if [[ $fails -gt 0 ]]; then - echo "$fails scenario(s) failed to evaluate. A combined scenario bundles several" - echo "toggles — re-run the suspects individually for the trace, e.g.:" - echo " nix eval --impure --show-trace --expr 'let f = builtins.getFlake (toString ./.); in (f.nixosConfigurations.default.extendModules { modules = [ { <the toggle> } ]; }).config.system.build.toplevel.drvPath'" - exit 1 -fi -echo "All scenarios evaluated cleanly." diff --git a/core/branding/Nomarchy.ttf b/core/branding/Nomarchy.ttf deleted file mode 100644 index b09cd9f..0000000 Binary files a/core/branding/Nomarchy.ttf and /dev/null differ diff --git a/core/branding/about.txt b/core/branding/about.txt deleted file mode 100644 index caf7c78..0000000 --- a/core/branding/about.txt +++ /dev/null @@ -1,16 +0,0 @@ -# About Nomarchy - -Nomarchy is a highly curated, NixOS-based distribution designed for power users. -It features a customized Hyprland desktop environment with a declarative -theming engine and a suite of integrated utility scripts. - -Built on a foundation of: -- NixOS (Linux) -- Hyprland (Window Manager) -- Waybar (Status Bar) -- Walker (Application Launcher & Menu) -- Stylix (Theming Engine) - -Version: 2026.05.04 -Docs: https://github.com/nomarchy/nomarchy/docs -Manual: nomarchy-manual (Command) diff --git a/core/branding/icon.png b/core/branding/icon.png deleted file mode 100644 index dbcde56..0000000 Binary files a/core/branding/icon.png and /dev/null differ diff --git a/core/branding/icon.svg b/core/branding/icon.svg deleted file mode 100644 index 4171d84..0000000 --- a/core/branding/icon.svg +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - width="210mm" - height="297mm" - viewBox="0 0 210 297" - version="1.1" - id="svg1" - xml:space="preserve" - inkscape:version="1.4.3 (0d15f75042, 2025-12-25)" - sodipodi:docname="icon.svg" - inkscape:export-filename="logo.svg" - inkscape:export-xdpi="96" - inkscape:export-ydpi="96" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview - id="namedview1" - pagecolor="#ffffff" - bordercolor="#000000" - borderopacity="0.25" - inkscape:showpageshadow="2" - inkscape:pageopacity="0.0" - inkscape:pagecheckerboard="0" - inkscape:deskcolor="#d1d1d1" - inkscape:document-units="mm" - inkscape:zoom="0.82803284" - inkscape:cx="215.57116" - inkscape:cy="452.27675" - inkscape:window-width="1025" - inkscape:window-height="1012" - inkscape:window-x="0" - inkscape:window-y="0" - inkscape:window-maximized="0" - inkscape:current-layer="layer1" /><defs - id="defs1" /><g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1"><g - id="g4" - transform="translate(3.1953242,-22.686801)" - style="fill:#000088;fill-opacity:1"><path - style="fill:#000088;stroke-width:0.264583;fill-opacity:1" - d="M 25.136891,85.823024 25.557592,210.77125 88.452409,174.38061 67.417351,160.49747 57.110174,166.38729 V 105.80633 Z" - id="path1" /><path - style="fill:#000088;stroke-width:0.264583;fill-opacity:1" - d="M 67.728991,112.41131 182.54178,185.60757 153.16137,202.85259 67.830432,148.17947 Z" - id="path2" /><path - style="fill:#000088;stroke-width:0.264583;fill-opacity:1" - d="M 139.74857,145.88014 140.00405,110.4959 54.800856,56.333749 25.675926,73.32329 Z" - id="path3" /><path - style="fill:#000088;stroke-width:0.264583;fill-opacity:1" - d="M 182.2863,172.70573 V 48.286069 l -62.59305,36.406166 20.82177,13.668277 10.21927,-5.74834 0.12774,60.165978 z" - id="path4" /></g></g></svg> diff --git a/core/branding/icon.txt b/core/branding/icon.txt deleted file mode 100644 index 93fd686..0000000 --- a/core/branding/icon.txt +++ /dev/null @@ -1,34 +0,0 @@ - ${2},,, - ${1},, ${2},,,,,, - ${1},,,,,,,, ${2},,,,,,,,,, - ${1},,,,,,,,,,,,,, ${2}.,,,,,,,,,,,, - ${1},,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,, -${1},,,,,,,,,,,,,,,,,,,,,,,. ${2},,,,,,,,,,,,,,,,,,, - ${1}.,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,,,,, -${1}, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,,,,,, -${1},,, ${1},,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,,,,,, -${1},,,,,, ${1}.,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,, -${1},,,,,,,,,. ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,, ${2}.,,,,,,,,,,,, -${1},,,,,,,,,,,, ${1}.,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,. ${2},,,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},, ${1},,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,, ${1},,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,, ${1},,,,,,,,,,,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,, ${1},,,,,,,,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,, ${1},,,,,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,, ${1},,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,, ${1},,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,, ${1},, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,, -${1},,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,, ${2}.,,,,, -${1},,,,,,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,, -${1},,,,,,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, -${1},,,,,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, -${1},,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,, -${1},,,,,,,,,,,,,,,, ${1}.,,,,,,,,,,,,,,,,,,, -${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,, -${1},,,,,,,,,, ${1},,,,,,,,,. -${1},,,,,, ${1},, -${1},,,, diff --git a/core/branding/logo.png b/core/branding/logo.png deleted file mode 100644 index dbcde56..0000000 Binary files a/core/branding/logo.png and /dev/null differ diff --git a/core/branding/logo.svg b/core/branding/logo.svg deleted file mode 100644 index b060ba0..0000000 --- a/core/branding/logo.svg +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - width="157.40488mm" - height="162.48518mm" - viewBox="0 0 157.40488 162.48518" - version="1.1" - id="svg1" - xml:space="preserve" - inkscape:version="1.4.3 (0d15f75042, 2025-12-25)" - sodipodi:docname="icon.svg" - inkscape:export-filename="logo.png" - inkscape:export-xdpi="96" - inkscape:export-ydpi="96" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview - id="namedview1" - pagecolor="#ffffff" - bordercolor="#000000" - borderopacity="0.25" - inkscape:showpageshadow="2" - inkscape:pageopacity="0.0" - inkscape:pagecheckerboard="0" - inkscape:deskcolor="#d1d1d1" - inkscape:document-units="mm" - inkscape:zoom="0.82803284" - inkscape:cx="216.175" - inkscape:cy="452.27675" - inkscape:window-width="1914" - inkscape:window-height="1012" - inkscape:window-x="0" - inkscape:window-y="0" - inkscape:window-maximized="0" - inkscape:current-layer="layer1"><inkscape:page - x="0" - y="-1.1741086e-21" - width="157.40488" - height="162.48518" - id="page2" - margin="0" - bleed="0" /></sodipodi:namedview><defs - id="defs1" /><g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-28.332214,-25.599269)"><g - id="g4" - transform="translate(3.1953242,-22.686801)" - style="fill:#000088;fill-opacity:1"><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 25.136891,85.823024 25.557592,210.77125 88.452409,174.38061 67.417351,160.49747 57.110174,166.38729 V 105.80633 Z" - id="path1" /><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 67.728991,112.41131 182.54178,185.60757 153.16137,202.85259 67.830432,148.17947 Z" - id="path2" /><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 139.74857,145.88014 140.00405,110.4959 54.800856,56.333749 25.675926,73.32329 Z" - id="path3" /><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 182.2863,172.70573 V 48.286069 l -62.59305,36.406166 20.82177,13.668277 10.21927,-5.74834 0.12774,60.165978 z" - id="path4" /></g></g></svg> diff --git a/core/branding/logo.txt b/core/branding/logo.txt deleted file mode 100644 index 0bdd125..0000000 --- a/core/branding/logo.txt +++ /dev/null @@ -1,34 +0,0 @@ - ,,, - ,, ,,,,,, - ,,,,,,,, ,,,,,,,,,, - ,,,,,,,,,,,,,, .,,,,,,,,,,,, - ,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,,,, - .,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,, -, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,, -,,, ,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,, -,,,,,, .,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,, -,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,,,,,,, ,,, .,,,,,,,,,,,, -,,,,,,,,,,,, .,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,, -,,,,,,,,,,,,, ,, ,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,, ,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,, ,,,,,,,,,,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,, ,,,,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,,,, ,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,, ,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,, ,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,, -,,,,,,,,,,,,, ,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,, -,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,, .,,,,, -,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,, -,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,,,,,, -,,,,,,,,,,,,, ,,,,,,,,,,,,,, -,,,,,,,,,, ,,,,,,,,,. -,,,,,, ,, -,,,, diff --git a/core/branding/screensaver.txt b/core/branding/screensaver.txt deleted file mode 100644 index 5489450..0000000 --- a/core/branding/screensaver.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Nomarchy Screensaver Configuration - -Nomarchy uses hyprlock for locking and hypridle for idle management. -To configure the screensaver/lock screen visuals, edit: -~/.config/hypr/hyprlock.conf - -To configure idle timeouts and actions, edit: -~/.config/hypr/hypridle.conf - -You can also toggle the screensaver/idle management via the Nomarchy Menu: -Trigger > Toggle > Idle Lock diff --git a/core/default.nix b/core/default.nix deleted file mode 100644 index 0f9b667..0000000 --- a/core/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ ... }: - -{ - imports = [ ./system ]; -} diff --git a/core/home/accessibility.nix b/core/home/accessibility.nix deleted file mode 100644 index a49679c..0000000 --- a/core/home/accessibility.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ config, lib, ... }: - -let - cfg = config.nomarchy.accessibility; -in -{ - config = lib.mkIf cfg.enable { - # Hyprland-side accessibility extras. The system preset - # (core/system/accessibility.nix) covers AT-SPI2 + Orca on PATH + - # XCURSOR_SIZE; this module adds the bits Hyprland reads directly. - # - # Loaded via extraConfig (mkAfter) so it merges with — and overrides - # — the templated input.conf for the repeat-rate / repeat-delay - # fields. The Orca keybinding is additive. - wayland.windowManager.hyprland.extraConfig = lib.mkAfter '' - # Accessibility — slower key-repeat so holding a key isn't a - # runaway machine-gun for users with low-mobility hands. - input { - repeat_rate = 25 - repeat_delay = 1000 - } - - # Launch the Orca screen reader. The system preset puts `orca` - # on PATH when nomarchy.system.accessibility.enable = true. - bindd = SUPER ALT, S, Launch Orca, exec, orca - ''; - }; -} diff --git a/core/home/bash.nix b/core/home/bash.nix deleted file mode 100644 index 548f172..0000000 --- a/core/home/bash.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ config, lib, ... }: - -{ - programs.bash = { - enable = true; - - bashrcExtra = '' - if [[ -f ~/.config/nomarchy/default/bash/rc ]]; then - source ~/.config/nomarchy/default/bash/rc - fi - ''; - - shellAliases = lib.mkDefault { - # File system - lsa = "ls -a"; - - # Directories - ".." = "cd .."; - "..." = "cd ../.."; - "...." = "cd ../../.."; - - # Tools - c = "opencode"; - d = "docker"; - r = "rails"; - t = "tmux attach || tmux new -s Work"; - - # Git - g = "git"; - gcm = "git commit -m"; - gcam = "git commit -a -m"; - gcad = "git commit -a --amend"; - - # NixOS commands - sys-update = "nomarchy-sys-update"; - env-update = "nomarchy-env-update"; - }; - }; -} diff --git a/core/home/config/brave-flags.conf b/core/home/config/brave-flags.conf deleted file mode 100644 index 171cc79..0000000 --- a/core/home/config/brave-flags.conf +++ /dev/null @@ -1,4 +0,0 @@ ---ozone-platform=wayland ---ozone-platform-hint=wayland ---enable-features=TouchpadOverscrollHistoryNavigation ---load-extension=~/.config/nomarchy/default/chromium/extensions/copy-url diff --git a/core/home/config/chromium-flags.conf b/core/home/config/chromium-flags.conf deleted file mode 100644 index 171cc79..0000000 --- a/core/home/config/chromium-flags.conf +++ /dev/null @@ -1,4 +0,0 @@ ---ozone-platform=wayland ---ozone-platform-hint=wayland ---enable-features=TouchpadOverscrollHistoryNavigation ---load-extension=~/.config/nomarchy/default/chromium/extensions/copy-url diff --git a/core/home/config/fastfetch/config.jsonc b/core/home/config/fastfetch/config.jsonc deleted file mode 100644 index 67fe88b..0000000 --- a/core/home/config/fastfetch/config.jsonc +++ /dev/null @@ -1,137 +0,0 @@ -{ - "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", - "logo": { - "type": "file", - "source": "~/.config/nomarchy/branding/icon.txt", - "color": { "1": "green", "2": "blue" }, - "padding": { - "top": 2, - "right": 6, - "left": 2 - } - }, - "modules": [ - "break", - { - "type": "custom", - "format": "\u001b[90m┌──────────────────────Hardware──────────────────────┐" - }, - { - "type": "host", - "key": " PC", - "keyColor": "green" - }, - { - "type": "cpu", - "key": "│ ├", - "showPeCoreCount": true, - "keyColor": "green" - }, - { - "type": "gpu", - "key": "│ ├", - "detectionMethod": "pci", - "keyColor": "green" - }, - { - "type": "display", - "key": "│ ├󱄄", - "keyColor": "green" - }, - { - "type": "disk", - "key": "│ ├󰋊", - "keyColor": "green" - }, - { - "type": "memory", - "key": "│ ├", - "keyColor": "green" - }, - { - "type": "swap", - "key": "└ └󰓡 ", - "keyColor": "green" - }, - { - "type": "custom", - "format": "\u001b[90m└────────────────────────────────────────────────────┘" - }, - "break", - { - "type": "custom", - "format": "\u001b[90m┌──────────────────────Software──────────────────────┐" - }, - { - "type": "os", - "key": "\ue900 OS", - "keyColor": "blue" - }, - { - "type": "kernel", - "key": "│ ├", - "keyColor": "blue" - }, - { - "type": "wm", - "key": "│ ├", - "keyColor": "blue" - }, - { - "type": "de", - "key": " DE", - "keyColor": "blue" - }, - { - "type": "terminal", - "key": "│ ├", - "keyColor": "blue" - }, - { - "type": "packages", - "key": "│ ├󰏖", - "keyColor": "blue" - }, - { - "type": "wmtheme", - "key": "│ ├󰉼", - "keyColor": "blue" - }, - { - "type": "command", - "key": "│ ├󰸌", - "keyColor": "blue", - "text": "theme=$(cat ~/.config/nomarchy/current/theme.name); echo -e \"$theme \\e[38m●\\e[37m●\\e[36m●\\e[35m●\\e[34m●\\e[33m●\\e[32m●\\e[31m●\"" - }, - { - "type": "terminalfont", - "key": "└ └", - "keyColor": "blue" - }, - { - "type": "custom", - "format": "\u001b[90m└────────────────────────────────────────────────────┘" - }, - "break", - { - "type": "custom", - "format": "\u001b[90m┌────────────────Age / Uptime / Update───────────────┐" - }, - { - "type": "command", - "key": "󱦟 OS Age", - "keyColor": "magenta", - "text": "echo $(( ($(date +%s) - $(stat -c %W /)) / 86400 )) days" - }, - { - "type": "uptime", - "key": "󱫐 Uptime", - "keyColor": "magenta" - }, - { - "type": "custom", - "format": "\u001b[90m└────────────────────────────────────────────────────┘" - }, - "break" - ] -} diff --git a/core/home/config/fcitx5/conf/clipboard.conf b/core/home/config/fcitx5/conf/clipboard.conf deleted file mode 100644 index 8aef5a8..0000000 --- a/core/home/config/fcitx5/conf/clipboard.conf +++ /dev/null @@ -1,2 +0,0 @@ -TriggerKey= -PastePrimaryKey= diff --git a/core/home/config/fcitx5/conf/xcb.conf b/core/home/config/fcitx5/conf/xcb.conf deleted file mode 100644 index 90f5316..0000000 --- a/core/home/config/fcitx5/conf/xcb.conf +++ /dev/null @@ -1 +0,0 @@ -Allow Overriding System XKB Settings=False diff --git a/core/home/config/fontconfig/fonts.conf b/core/home/config/fontconfig/fonts.conf deleted file mode 100644 index 2d87376..0000000 --- a/core/home/config/fontconfig/fonts.conf +++ /dev/null @@ -1,79 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> -<fontconfig> - <match target="pattern"> - <test name="family" qual="any"> - <string>sans-serif</string> - </test> - <edit name="family" mode="assign" binding="strong"> - <string>Liberation Sans</string> - </edit> - </match> - - <match target="pattern"> - <test name="family" qual="any"> - <string>serif</string> - </test> - <edit name="family" mode="assign" binding="strong"> - <string>Liberation Serif</string> - </edit> - </match> - - <match target="pattern"> - <test name="family" qual="any"> - <string>monospace</string> - </test> - <edit name="family" mode="assign" binding="strong"> - <string>JetBrainsMono Nerd Font</string> - </edit> - </match> - - <alias> - <family>system-ui</family> - <prefer> - <family>Liberation Sans</family> - </prefer> - </alias> - - <alias> - <family>ui-monospace</family> - <default> - <family>monospace</family> - </default> - </alias> - - <alias> - <family>-apple-system</family> - <prefer> - <family>Liberation Sans</family> - </prefer> - </alias> - - <alias> - <family>BlinkMacSystemFont</family> - <prefer> - <family>Liberation Sans</family> - </prefer> - </alias> - - <alias> - <family>sans-serif</family> - <accept> - <family>Noto Color Emoji</family> - </accept> - </alias> - - <alias> - <family>serif</family> - <accept> - <family>Noto Color Emoji</family> - </accept> - </alias> - - <alias> - <family>monospace</family> - <accept> - <family>Noto Color Emoji</family> - </accept> - </alias> -</fontconfig> diff --git a/core/home/config/git/config b/core/home/config/git/config deleted file mode 100644 index 0f8e979..0000000 --- a/core/home/config/git/config +++ /dev/null @@ -1,28 +0,0 @@ -# See https://git-scm.com/docs/git-config - -[alias] - co = checkout - br = branch - ci = commit - st = status -[init] - defaultBranch = master -[pull] - rebase = true # Rebase (instead of merge) on pull -[push] - autoSetupRemote = true # Automatically set upstream branch on push -[diff] - algorithm = histogram # Clearer diffs on moved/edited lines - colorMoved = plain # Highlight moved blocks in diffs - mnemonicPrefix = true # More intuitive refs in diff output -[commit] - verbose = true # Include diff comment in commit message template -[column] - ui = auto # Output in columns when possible -[branch] - sort = -committerdate # Sort branches by most recent commit first -[tag] - sort = -version:refname # Sort version numbers as you would expect -[rerere] - enabled = true # Record and reuse conflict resolutions - autoupdate = true # Apply stored conflict resolutions automatically diff --git a/core/home/config/imv/config b/core/home/config/imv/config deleted file mode 100644 index 4649324..0000000 --- a/core/home/config/imv/config +++ /dev/null @@ -1,13 +0,0 @@ -[binds] - -# Print the current image file -<Ctrl+p> = exec lp "$imv_current_file" - -# Delete the current image and quit the viewer -<Ctrl+x> = exec rm "$imv_current_file"; quit - -# Delete the current image and move to the next one -<Ctrl+Shift+X> = exec rm "$imv_current_file"; close - -# Rotate the currently open image by 90 degrees -<Ctrl+r> = exec mogrify -rotate 90 "$imv_current_file" diff --git a/core/home/config/nautilus-python/extensions/localsend.py b/core/home/config/nautilus-python/extensions/localsend.py deleted file mode 100644 index aaf4440..0000000 --- a/core/home/config/nautilus-python/extensions/localsend.py +++ /dev/null @@ -1,84 +0,0 @@ -import os -import shutil - -from gi import require_version - -require_version("Nautilus", "4.1") - -from gi.repository import GObject, Gio, Nautilus - - -class SendViaLocalSendAction(GObject.GObject, Nautilus.MenuProvider): - def _launch_localsend(self, paths): - command = self._resolve_command() - if not command: - return - - if command[-1] == "@@": - command = command + paths + ["@@"] - else: - command = command + paths - - Gio.Subprocess.new(command, Gio.SubprocessFlags.NONE) - - def _resolve_command(self): - localsend = shutil.which("localsend") - if localsend: - return [localsend, "--headless", "send"] - - flatpak = shutil.which("flatpak") - if flatpak and self._has_flatpak_app(flatpak, "org.localsend.localsend_app"): - return [ - flatpak, - "run", - "--file-forwarding", - "org.localsend.localsend_app", - "@@", - ] - - return None - - def _has_flatpak_app(self, flatpak, app_id): - process = Gio.Subprocess.new( - [flatpak, "info", app_id], - Gio.SubprocessFlags.STDOUT_SILENCE | Gio.SubprocessFlags.STDERR_SILENCE, - ) - return process.wait_check() - - def _selected_paths(self, files): - paths = [] - - for file in files: - location = file.get_location() - if not location: - continue - - path = location.get_path() - if path and path not in paths: - paths.append(path) - - return paths - - def _make_item(self, paths): - label = ( - "Send via LocalSend" if len(paths) == 1 else "Send selected via LocalSend" - ) - item = Nautilus.MenuItem( - name="LocalSendNautilus::send_via_localsend", - label=label, - icon="localsend", - ) - item.connect("activate", self._on_activate, paths) - return item - - def _on_activate(self, _menu, paths): - self._launch_localsend(paths) - - def get_file_items(self, *args): - files = args[0] if len(args) == 1 else args[1] - paths = self._selected_paths(files) - - if not paths or not self._resolve_command(): - return [] - - return [self._make_item(paths)] diff --git a/core/home/config/nomarchy-skill/SKILL.md b/core/home/config/nomarchy-skill/SKILL.md deleted file mode 100644 index d03f7c2..0000000 --- a/core/home/config/nomarchy-skill/SKILL.md +++ /dev/null @@ -1,399 +0,0 @@ ---- -name: nomarchy -description: > - REQUIRED for end-user customization of Linux desktop, window manager, or system config. - Use when editing ~/.config/hypr/, ~/.config/waybar/, ~/.config/walker/, - ~/.config/alacritty/, ~/.config/kitty/, ~/.config/ghostty/, ~/.config/mako/, - or ~/.config/nomarchy/. Triggers: Hyprland, window rules, animations, keybindings, - monitors, gaps, borders, blur, opacity, waybar, walker, terminal config, themes, - wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace - settings, display config, and user-facing nomarchy commands. Excludes Nomarchy - source development in ~/.local/share/nomarchy/ and repo-internal workflows. ---- - -# Nomarchy Skill - -Manage [Nomarchy](https://nomarchy.org/) Linux systems - a beautiful, modern, opinionated Arch Linux distribution with Hyprland. - -This skill is for end-user customization on installed systems. -It is not for contributing to Nomarchy source code. - -## When This Skill MUST Be Used - -**ALWAYS invoke this skill for end-user requests involving ANY of these:** - -- Editing ANY file in `~/.config/hypr/` (window rules, animations, keybindings, monitors, etc.) -- Editing ANY file in `~/.config/waybar/`, `~/.config/walker/`, `~/.config/mako/` -- Editing terminal configs (alacritty, kitty, ghostty) -- Editing ANY file in `~/.config/nomarchy/` -- Window behavior, animations, opacity, blur, gaps, borders -- Layer rules, workspace settings, display/monitor configuration -- Themes, wallpapers, fonts, appearance changes -- User-facing `nomarchy-*` commands (`nomarchy-theme-*`, `nomarchy-refresh-*`, `nomarchy-restart-*`, etc.) -- Screenshots, screen recording, night light, idle behavior, lock screen - -**If you're about to edit a config file in ~/.config/ on this system, STOP and use this skill first.** - -**Do NOT use this skill for Nomarchy development tasks** — editing files in `~/.local/share/nomarchy/` or modifying repo internals. - -## Critical Safety Rules - -**For end-user customization tasks, NEVER modify anything in `~/.local/share/nomarchy/`** - but READING is safe and encouraged. - -This directory contains Nomarchy's source files managed by git. Any changes will be: -- Lost on next `nomarchy-update` -- Cause conflicts with upstream -- Break the system's update mechanism - -``` -~/.local/share/nomarchy/ # READ-ONLY - NEVER EDIT (reading is OK) -├── bin/ # Source scripts (symlinked to PATH) -├── config/ # Default config templates -├── themes/ # Stock themes -├── default/ # System defaults -├── migrations/ # Update migrations -└── install/ # Installation scripts -``` - -**Reading `~/.local/share/nomarchy/` is SAFE and useful** - do it freely to: -- Understand how nomarchy commands work: `cat $(which nomarchy-theme-set)` -- See default configs before customizing: `cat ~/.local/share/nomarchy/config/fastfetch/config.jsonc` -- Check stock theme files to copy for customization -- Reference default hyprland settings: `cat ~/.config/nomarchy/default/hypr/*` - -**Always use these safe locations instead:** -- `~/.config/` - User configuration (safe to edit) -- `~/.config/nomarchy/themes/<custom-name>/` - Custom themes (must be real directories) -- `~/.config/nomarchy/hooks/` - Custom automation hooks - -If the request is to develop Nomarchy itself, this skill is out of scope. Follow repository development instructions instead of this skill. - -## System Architecture - -Nomarchy is built on: - -| Component | Purpose | Config Location | -|-----------|---------|-----------------| -| **Arch Linux** | Base OS | `/etc/`, `~/.config/` | -| **Hyprland** | Wayland compositor/WM | `~/.config/hypr/` | -| **Waybar** | Status bar | `~/.config/waybar/` | -| **Walker** | App launcher | `~/.config/walker/` | -| **Alacritty/Kitty/Ghostty** | Terminals | `~/.config/<terminal>/` | -| **Mako** | Notifications | `~/.config/mako/` | -| **SwayOSD** | On-screen display | `~/.config/swayosd/` | - -## Command Discovery - -Nomarchy provides ~145 commands following `nomarchy-<category>-<action>` pattern. - -```bash -# List all nomarchy commands -compgen -c | grep -E '^nomarchy-' | sort -u - -# Find commands by category -compgen -c | grep -E '^nomarchy-theme' -compgen -c | grep -E '^nomarchy-restart' - -# Read a command's source to understand it -cat $(which nomarchy-theme-set) -``` - -### Command Categories - -| Prefix | Purpose | Example | -|--------|---------|---------| -| `nomarchy-refresh-*` | Reset config to defaults (backs up first) | `nomarchy-refresh-waybar` | -| `nomarchy-restart-*` | Restart a service/app | `nomarchy-restart-waybar` | -| `nomarchy-toggle-*` | Toggle feature on/off | `nomarchy-toggle-nightlight` | -| `nomarchy-theme-*` | Theme management | `nomarchy-theme-set <name>` | -| `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-add <pkg>` | -| `nomarchy-setup-*` | Initial setup tasks | `nomarchy-setup-fingerprint` | -| `nomarchy-update-*` | System updates | `nomarchy-update` | - -## Configuration Locations - -### Hyprland (Window Manager) - -``` -~/.config/hypr/ -├── hyprland.conf # Main config (sources others) -├── bindings.conf # Keybindings -├── monitors.conf # Display configuration -├── input.conf # Keyboard/mouse settings -├── looknfeel.conf # Appearance (gaps, borders, animations) -├── envs.conf # Environment variables -├── autostart.conf # Startup applications -├── hypridle.conf # Idle behavior (screen off, lock, suspend) -├── hyprlock.conf # Lock screen appearance -└── hyprsunset.conf # Night light / blue light filter -``` - -**Key behaviors:** -- Hyprland auto-reloads on config save (no restart needed for most changes) -- Use `hyprctl reload` to force reload -- Use `nomarchy-refresh-hyprland` to reset to defaults - -### Waybar (Status Bar) - -``` -~/.config/waybar/ -├── config.jsonc # Bar layout and modules (JSONC format) -└── style.css # Styling -``` - -**Waybar does NOT auto-reload.** You MUST run `nomarchy-restart-waybar` after any config changes. - -**Commands:** `nomarchy-restart-waybar`, `nomarchy-refresh-waybar`, `nomarchy-toggle-waybar` - -### Terminals - -``` -~/.config/alacritty/alacritty.toml -~/.config/kitty/kitty.conf -~/.config/ghostty/config -``` - -**Command:** `nomarchy-restart-terminal` - -### Other Configs - -| App | Location | -|-----|----------| -| btop | `~/.config/btop/btop.conf` | -| fastfetch | `~/.config/fastfetch/config.jsonc` | -| lazygit | `~/.config/lazygit/config.yml` | -| starship | `~/.config/starship.toml` | -| git | `~/.config/git/config` | -| walker | `~/.config/walker/config.toml` | - -## Safe Customization Patterns - -### Pattern 1: Edit User Config Directly - -For simple changes, edit files in `~/.config/`: - -```bash -# 1. Read current config -cat ~/.config/hypr/bindings.conf - -# 2. Backup before changes -cp ~/.config/hypr/bindings.conf ~/.config/hypr/bindings.conf.bak.$(date +%s) - -# 3. Make changes with Edit tool - -# 4. Apply changes -# - Hyprland: auto-reloads on save (no restart needed) -# - Waybar: MUST restart with nomarchy-restart-waybar -# - Walker: MUST restart with nomarchy-restart-walker -# - Terminals: MUST restart with nomarchy-restart-terminal -``` - -### Pattern 2: Make a new theme - -1. Create a directory under ~/.config/nomarchy/themes. -2. See how an existing theme is done via ~/.local/share/nomarchy/themes/catppuccin. -3. Download a matching background (or several) from the internet and put them in ~/.config/nomarchy/themes/[name-of-new-theme] -4. When done with the theme, run nomarchy-theme-set "Name of new theme" - -### Pattern 3: Use Hooks for Automation - -Create scripts in `~/.config/nomarchy/hooks/` to run automatically on events: - -```bash -# Available hooks (see samples in ~/.config/nomarchy/hooks/): -~/.config/nomarchy/hooks/ -├── theme-set # Runs after theme change (receives theme name as $1) -├── font-set # Runs after font change -└── post-update # Runs after nomarchy-update -``` - -Example hook (`~/.config/nomarchy/hooks/theme-set`): -```bash -#!/bin/bash -THEME_NAME=$1 -echo "Theme changed to: $THEME_NAME" -# Add custom actions here -``` - -### Pattern 4: Reset to Defaults -- ALWAYS SEEK USER CONFIRMATION BEFORE RUNNING - -When customizations go wrong: - -```bash -# Reset specific config (creates backup automatically) -nomarchy-refresh-waybar -nomarchy-refresh-hyprland - -# The refresh command: -# 1. Backs up current config with timestamp -# 2. Copies default from ~/.local/share/nomarchy/config/ -# 3. Restarts the component -``` - -## Common Tasks - -### Themes - -```bash -nomarchy-theme-list # Show available themes -nomarchy-theme-current # Show current theme -nomarchy-theme-set <name> # Apply theme (use "Tokyo Night" not "tokyo-night") -nomarchy-theme-next # Cycle to next theme -nomarchy-theme-bg-next # Cycle wallpaper -nomarchy-theme-install <url> # Install from git repo -nomarchy-theme-remove <name> # Remove an installed extra theme -nomarchy-theme-refresh # Re-apply current theme from templates -nomarchy-theme-bg-install # Open backgrounds dir to drop in custom images -``` - -### Keybindings - -Edit `~/.config/hypr/bindings.conf`. Format: -``` -bind = SUPER, Return, exec, xdg-terminal-exec -bind = SUPER, Q, killactive -bind = SUPER SHIFT, E, exit -``` - -View current bindings: `nomarchy-menu-keybindings --print` - -**IMPORTANT: When re-binding an existing key:** - -1. First check existing bindings: `nomarchy-menu-keybindings --print` -2. If the key is already bound, you MUST add an `unbind` directive BEFORE your new `bind` -3. Inform the user what the key was previously bound to - -Example - rebinding SUPER+F (which is bound to fullscreen by default): -``` -# Unbind existing SUPER+F (was: fullscreen) -unbind = SUPER, F -# New binding for file manager -bind = SUPER, F, exec, nautilus -``` - -Always tell the user: "Note: SUPER+F was previously bound to fullscreen. I've added an unbind directive to override it." - -### Display/Monitors - -Edit `~/.config/hypr/monitors.conf`. Format: -``` -monitor = eDP-1, 1920x1080@60, 0x0, 1 -monitor = HDMI-A-1, 2560x1440@144, 1920x0, 1 -``` - -List monitors: `hyprctl monitors` - -### Window Rules - -**CRITICAL: Hyprland window rules syntax changes frequently between versions.** - -Before writing ANY window rules, you MUST fetch the current documentation from the official Hyprland wiki: -- https://github.com/hyprwm/hyprland-wiki/blob/main/content/Configuring/Window-Rules.md - -DO NOT rely on cached or memorized window rule syntax. The format has changed multiple times and using outdated syntax will cause errors or unexpected behavior. - -Window rules go in `~/.config/hypr/hyprland.conf` or a sourced file. Always verify the current syntax from the wiki first. - -### Fonts - -```bash -nomarchy-font-list # Available fonts -nomarchy-font-current # Current font -nomarchy-font-set <name> # Change font -``` - -### System - -```bash -nomarchy-update # Full system update -nomarchy-version # Show Nomarchy version -nomarchy-debug --no-sudo --print # Debug info (ALWAYS use these flags) -nomarchy-lock-screen # Lock screen -nomarchy-system-shutdown # Shutdown -nomarchy-system-reboot # Reboot -nomarchy-sudo-passwordless-toggle # Toggle 15-min passwordless sudo -nomarchy-sudo-reset # Clear sudo lockout / faillock -nomarchy-restart-trackpad # Reload intel_quicki2c (fixes dead THC trackpad) -``` - -**IMPORTANT:** Always run `nomarchy-debug` with `--no-sudo --print` flags to avoid interactive sudo prompts that will hang the terminal. - -### Custom App Launchers - -```bash -nomarchy-webapp-install # Add a web app launcher (interactive) -nomarchy-webapp-remove [name...] # Remove web apps (interactive if no name) -nomarchy-webapp-remove-all # Bulk-remove every web app -nomarchy-tui-install # Add a TUI launcher for a terminal program -nomarchy-tui-remove [name...] # Remove TUI launchers -nomarchy-tui-remove-all # Bulk-remove every TUI launcher -``` - -Both families write `.desktop` files into `~/.local/share/applications/` so they appear in the app launcher (walker / rofi). - -### Virtualization - -```bash -nomarchy-windows-vm install # Provision a Windows VM via docker-compose -nomarchy-windows-vm launch # Connect to the VM (auto-stop on disconnect) -nomarchy-windows-vm launch -k # Connect, keep VM running after disconnect -nomarchy-windows-vm stop # Shut the VM down -nomarchy-windows-vm status # Show current state -``` - -Requires KVM (`/dev/kvm`) and Docker (enable via `nomarchy.system.virtualization.docker`). - -## Troubleshooting - -```bash -# Get debug information (ALWAYS use these flags to avoid interactive prompts) -nomarchy-debug --no-sudo --print - -# Upload logs for support -nomarchy-upload-log - -# Reset specific config to defaults -# Examples: nomarchy-refresh-fastfetch, nomarchy-refresh-hyprland, nomarchy-refresh-waybar -nomarchy-refresh-<app> - -# Refresh specific config file -# config-file path is relative to ~/.config/; the stock copy must exist under -# ~/.local/share/nomarchy/config/ (i.e. a core/home/config item). -# eg. nomarchy-refresh-config fastfetch/config.jsonc refreshes ~/.config/fastfetch/config.jsonc -nomarchy-refresh-config <config-file> - -# Full reinstall of configs (nuclear option) -nomarchy-reinstall -``` - -## Decision Framework - -When user requests system changes: - -1. **Is it a stock nomarchy command?** Use it directly -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` (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 - -This skill intentionally does not cover Nomarchy source development. Do not use this skill for: -- Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.) -- Creating or editing migrations -- Modifying Nomarchy's own source tree - -## Example Requests - -- "Change my theme to catppuccin" -> `nomarchy-theme-set catppuccin` -- "Add a keybinding for Super+E to open file manager" -> Check existing bindings first, add `unbind` if needed, then add `bind` in `~/.config/hypr/bindings.conf` -- "Configure my external monitor" -> Edit `~/.config/hypr/monitors.conf` -- "Make the window gaps smaller" -> Edit `~/.config/hypr/looknfeel.conf` -- "Set up night light to turn on at sunset" -> `nomarchy-toggle-nightlight` or edit `~/.config/hypr/hyprsunset.conf` -- "Customize the catppuccin theme colors" -> Create `~/.config/nomarchy/themes/catppuccin-custom/` by copying from stock, then edit -- "Run a script every time I change themes" -> Create `~/.config/nomarchy/hooks/theme-set` -- "Reset waybar to defaults" -> `nomarchy-refresh-waybar` diff --git a/core/home/config/nomarchy/default/alacritty/screensaver.toml b/core/home/config/nomarchy/default/alacritty/screensaver.toml deleted file mode 100644 index 7db389e..0000000 --- a/core/home/config/nomarchy/default/alacritty/screensaver.toml +++ /dev/null @@ -1,11 +0,0 @@ -[colors.primary] -background = "0x000000" - -[colors.cursor] -cursor = "0x000000" - -[font] -size = 18.0 - -[window] -opacity = 1.0 diff --git a/core/home/config/nomarchy/default/bash/aliases b/core/home/config/nomarchy/default/bash/aliases deleted file mode 100644 index 8e281d0..0000000 --- a/core/home/config/nomarchy/default/bash/aliases +++ /dev/null @@ -1,57 +0,0 @@ -# File system -if command -v eza &> /dev/null; then - alias ls='eza -lh --group-directories-first --icons=auto' - alias lsa='ls -a' - alias lt='eza --tree --level=2 --long --icons --git' - alias lta='lt -a' -fi - -if [[ "$TERM" == "xterm-kitty" ]]; then - alias ff="fzf --preview 'case \$(file --mime-type -b {}) in image/*) kitty icat --clear --transfer-mode=memory --stdin=no --place=\${FZF_PREVIEW_COLUMNS}x\${FZF_PREVIEW_LINES}@0x0 {} ;; *) bat --style=numbers --color=always {} ;; esac'" -else - alias ff="fzf --preview 'bat --style=numbers --color=always {}'" -fi -alias eff='$EDITOR "$(ff)"' -sff() { if [ $# -eq 0 ]; then echo "Usage: sff <destination> (e.g. sff host:/tmp/)"; return 1; fi; local file; file=$(find . -type f -printf '%T@\t%p\n' | sort -rn | cut -f2- | ff) && [ -n "$file" ] && scp "$file" "$1"; } - -if command -v zoxide &> /dev/null; then - alias cd="zd" - zd() { - if (( $# == 0 )); then - builtin cd ~ || return - elif [[ -d $1 ]]; then - builtin cd "$1" || return - else - if ! z "$@"; then - echo "Error: Directory not found" - return 1 - fi - - printf "\U000F17A9 " - pwd - fi - } -fi - -open() ( - xdg-open "$@" >/dev/null 2>&1 & -) - -# Directories -alias ..='cd ..' -alias ...='cd ../..' -alias ....='cd ../../..' - -# Tools -alias c='opencode' -alias cx='printf "\033[2J\033[3J\033[H" && claude --allow-dangerously-skip-permissions' -alias d='docker' -alias r='rails' -alias t='tmux attach || tmux new -s Work' -n() { if [ "$#" -eq 0 ]; then command nvim . ; else command nvim "$@"; fi; } - -# Git -alias g='git' -alias gcm='git commit -m' -alias gcam='git commit -a -m' -alias gcad='git commit -a --amend' diff --git a/core/home/config/nomarchy/default/bash/envs b/core/home/config/nomarchy/default/bash/envs deleted file mode 100644 index ec23f3d..0000000 --- a/core/home/config/nomarchy/default/bash/envs +++ /dev/null @@ -1,6 +0,0 @@ -# Editor used by CLI -export SUDO_EDITOR="$EDITOR" -export BAT_THEME=ansi - -# Duplicated from .config/uwsm/env so SSH works too -export PATH=$PATH:$HOME/.local/bin diff --git a/core/home/config/nomarchy/default/bash/fns/compression b/core/home/config/nomarchy/default/bash/fns/compression deleted file mode 100644 index 4e8bb81..0000000 --- a/core/home/config/nomarchy/default/bash/fns/compression +++ /dev/null @@ -1,3 +0,0 @@ -# Compression -compress() { tar -czf "${1%/}.tar.gz" "${1%/}"; } -alias decompress="tar -xzf" diff --git a/core/home/config/nomarchy/default/bash/fns/drives b/core/home/config/nomarchy/default/bash/fns/drives deleted file mode 100644 index ec7dbfe..0000000 --- a/core/home/config/nomarchy/default/bash/fns/drives +++ /dev/null @@ -1,59 +0,0 @@ -# Write iso file to sd card -iso2sd() { - if (( $# < 1 )); then - echo "Usage: iso2sd <input_file> [output_device]" - echo "Example: iso2sd ~/Downloads/ubuntu-25.04-desktop-amd64.iso /dev/sda" - return 1 - fi - - local iso="$1" - local drive="$2" - - if [[ -z $drive ]]; then - local available_sds=$(lsblk -dpno NAME | grep -E '/dev/sd') - - if [[ -z $available_sds ]]; then - echo "No SD drives found and no drive specified" - return 1 - fi - - drive=$(nomarchy-drive-select "$available_sds") - - if [[ -z $drive ]]; then - echo "No drive selected" - return 1 - fi - fi - - sudo dd bs=4M status=progress oflag=sync if="$iso" of="$drive" - sudo eject "$drive" -} - -# Format an entire drive for a single partition using exFAT -format-drive() { - if (( $# != 2 )); then - echo "Usage: format-drive <device> <name>" - echo "Example: format-drive /dev/sda 'My Stuff'" - echo -e "\nAvailable drives:" - lsblk -d -o NAME -n | awk '{print "/dev/"$1}' - else - echo "WARNING: This will completely erase all data on $1 and label it '$2'." - read -rp "Are you sure you want to continue? (y/N): " confirm - - if [[ $confirm =~ ^[Yy]$ ]]; then - sudo wipefs -a "$1" - sudo dd if=/dev/zero of="$1" bs=1M count=100 status=progress - sudo parted -s "$1" mklabel gpt - sudo parted -s "$1" mkpart primary 1MiB 100% - sudo parted -s "$1" set 1 msftdata on - - partition="$([[ $1 == *"nvme"* ]] && echo "${1}p1" || echo "${1}1")" - sudo partprobe "$1" || true - sudo udevadm settle || true - - sudo mkfs.exfat -n "$2" "$partition" - - echo "Drive $1 formatted as exFAT and labeled '$2'." - fi - fi -} diff --git a/core/home/config/nomarchy/default/bash/fns/ssh-port-forwarding b/core/home/config/nomarchy/default/bash/fns/ssh-port-forwarding deleted file mode 100644 index 4ee6c92..0000000 --- a/core/home/config/nomarchy/default/bash/fns/ssh-port-forwarding +++ /dev/null @@ -1,20 +0,0 @@ -# SSH Port Forwarding Functions -fip() { - (( $# < 2 )) && echo "Usage: fip <host> <port1> [port2] ..." && return 1 - local host="$1" - shift - for port in "$@"; do - ssh -f -N -L "$port:localhost:$port" "$host" && echo "Forwarding localhost:$port -> $host:$port" - done -} - -dip() { - (( $# == 0 )) && echo "Usage: dip <port1> [port2] ..." && return 1 - for port in "$@"; do - pkill -f "ssh.*-L $port:localhost:$port" && echo "Stopped forwarding port $port" || echo "No forwarding on port $port" - done -} - -lip() { - pgrep -af "ssh.*-L [0-9]+:localhost:[0-9]+" || echo "No active forwards" -} diff --git a/core/home/config/nomarchy/default/bash/fns/tmux b/core/home/config/nomarchy/default/bash/fns/tmux deleted file mode 100644 index 0144f2e..0000000 --- a/core/home/config/nomarchy/default/bash/fns/tmux +++ /dev/null @@ -1,97 +0,0 @@ -# Create a Tmux Dev Layout with editor, ai, and terminal -# Usage: tdl <c|cx|codex|other_ai> [<second_ai>] -tdl() { - [[ -z $1 ]] && { echo "Usage: tdl <c|cx|codex|other_ai> [<second_ai>]"; return 1; } - [[ -z $TMUX ]] && { echo "You must start tmux to use tdl."; return 1; } - - local current_dir="${PWD}" - local editor_pane ai_pane ai2_pane - local ai="$1" - local ai2="$2" - - # Use TMUX_PANE for the pane we're running in (stable even if active window changes) - editor_pane="$TMUX_PANE" - - # Name the current window after the base directory name - tmux rename-window -t "$editor_pane" "$(basename "$current_dir")" - - # Split window vertically - top 85%, bottom 15% (target editor pane explicitly) - tmux split-window -v -p 15 -t "$editor_pane" -c "$current_dir" - - # Split editor pane horizontally - AI on right 30% (capture new pane ID directly) - ai_pane=$(tmux split-window -h -p 30 -t "$editor_pane" -c "$current_dir" -P -F '#{pane_id}') - - # If second AI provided, split the AI pane vertically - if [[ -n $ai2 ]]; then - ai2_pane=$(tmux split-window -v -t "$ai_pane" -c "$current_dir" -P -F '#{pane_id}') - tmux send-keys -t "$ai2_pane" "$ai2" C-m - fi - - # Run ai in the right pane - tmux send-keys -t "$ai_pane" "$ai" C-m - - # Run nvim in the left pane - tmux send-keys -t "$editor_pane" "$EDITOR ." C-m - - # Select the nvim pane for focus - tmux select-pane -t "$editor_pane" -} - -# Create multiple tdl windows with one per subdirectory in the current directory -# Usage: tdlm <c|cx|codex|other_ai> [<second_ai>] -tdlm() { - [[ -z $1 ]] && { echo "Usage: tdlm <c|cx|codex|other_ai> [<second_ai>]"; return 1; } - [[ -z $TMUX ]] && { echo "You must start tmux to use tdlm."; return 1; } - - local ai="$1" - local ai2="$2" - local base_dir="$PWD" - local first=true - - # Rename the session to the current directory name (replace dots/colons which tmux disallows) - tmux rename-session "$(basename "$base_dir" | tr '.:' '--')" - - for dir in "$base_dir"/*/; do - [[ -d $dir ]] || continue - local dirpath="${dir%/}" - - if $first; then - # Reuse the current window for the first project - tmux send-keys -t "$TMUX_PANE" "cd '$dirpath' && tdl $ai $ai2" C-m - first=false - else - local pane_id=$(tmux new-window -c "$dirpath" -P -F '#{pane_id}') - tmux send-keys -t "$pane_id" "tdl $ai $ai2" C-m - fi - done -} - -# Create a multi-pane swarm layout with the same command started in each pane (great for AI) -# Usage: tsl <pane_count> <command> -tsl() { - [[ -z $1 || -z $2 ]] && { echo "Usage: tsl <pane_count> <command>"; return 1; } - [[ -z $TMUX ]] && { echo "You must start tmux to use tsl."; return 1; } - - local count="$1" - local cmd="$2" - local current_dir="${PWD}" - local -a panes - - tmux rename-window -t "$TMUX_PANE" "$(basename "$current_dir")" - - panes+=("$TMUX_PANE") - - while (( ${#panes[@]} < count )); do - local new_pane - local split_target="${panes[-1]}" - new_pane=$(tmux split-window -h -t "$split_target" -c "$current_dir" -P -F '#{pane_id}') - panes+=("$new_pane") - tmux select-layout -t "${panes[0]}" tiled - done - - for pane in "${panes[@]}"; do - tmux send-keys -t "$pane" "$cmd" C-m - done - - tmux select-pane -t "${panes[0]}" -} diff --git a/core/home/config/nomarchy/default/bash/fns/transcoding b/core/home/config/nomarchy/default/bash/fns/transcoding deleted file mode 100644 index 6f5b6a5..0000000 --- a/core/home/config/nomarchy/default/bash/fns/transcoding +++ /dev/null @@ -1,53 +0,0 @@ -# Transcode a video to a good-balance 1080p that's great for sharing online -transcode-video-1080p() { - ffmpeg -i "$1" -vf scale=1920:1080 -c:v libx264 -preset fast -crf 23 -c:a copy "${1%.*}-1080p.mp4" -} - -# Transcode a video to a good-balance 4K that's great for sharing online -transcode-video-4K() { - ffmpeg -i "$1" -c:v libx265 -preset slow -crf 24 -c:a aac -b:a 192k "${1%.*}-optimized.mp4" -} - -# Transcode any image to JPG image that's great for shrinking wallpapers -img2jpg() { - img="$1" - shift - - magick "$img" "$@" -quality 85 -strip "${img%.*}-converted.jpg" -} - -# Transcode any image to a small JPG (max 1080px wide) -img2jpg-small() { - img="$1" - shift - - magick "$img" "$@" -resize 1080x\> -quality 85 -strip "${img%.*}-small.jpg" -} - -# Transcode any image to a 4K JPG (max 2160px wide) -img2jpg-medium() { - img="$1" - shift - - magick "$img" "$@" -resize 2160x\> -quality 85 -strip "${img%.*}-medium.jpg" -} - -# Transcode any image to a 6K JPG (max 3160px wide) -img2jpg-large() { - img="$1" - shift - - magick "$img" "$@" -resize 3160x\> -quality 85 -strip "${img%.*}-large.jpg" -} - -# Transcode any image to compressed-but-lossless PNG -img2png() { - img="$1" - shift - - magick "$img" "$@" -strip -define png:compression-filter=5 \ - -define png:compression-level=9 \ - -define png:compression-strategy=1 \ - -define png:exclude-chunk=all \ - "${img%.*}-optimized.png" -} diff --git a/core/home/config/nomarchy/default/bash/fns/worktrees b/core/home/config/nomarchy/default/bash/fns/worktrees deleted file mode 100644 index a175a64..0000000 --- a/core/home/config/nomarchy/default/bash/fns/worktrees +++ /dev/null @@ -1,36 +0,0 @@ -# Create a new worktree and branch from within current git directory. -ga() { - if [[ -z "$1" ]]; then - echo "Usage: ga [branch name]" - return 1 - fi - - local branch="$1" - local base="$(basename "$PWD")" - local wt_path="../${base}--${branch}" - - git worktree add -b "$branch" "$wt_path" - mise trust "$wt_path" - cd "$wt_path" -} - -# Remove worktree and branch from within active worktree directory. -gd() { - if gum confirm "Remove worktree and branch?"; then - local cwd base branch root worktree - - cwd="$(pwd)" - worktree="$(basename "$cwd")" - - # split on first `--` - root="${worktree%%--*}" - branch="${worktree#*--}" - - # Protect against accidentally nuking a non-worktree directory - if [[ "$root" != "$worktree" ]]; then - cd "../$root" - git worktree remove "$cwd" --force || return 1 - git branch -D "$branch" - fi - fi -} diff --git a/core/home/config/nomarchy/default/bash/functions b/core/home/config/nomarchy/default/bash/functions deleted file mode 100644 index 9e2cef0..0000000 --- a/core/home/config/nomarchy/default/bash/functions +++ /dev/null @@ -1 +0,0 @@ -for f in $HOME/.config/nomarchy/default/bash/fns/*; do source "$f"; done diff --git a/core/home/config/nomarchy/default/bash/init b/core/home/config/nomarchy/default/bash/init deleted file mode 100644 index 4c5e6e3..0000000 --- a/core/home/config/nomarchy/default/bash/init +++ /dev/null @@ -1,24 +0,0 @@ -if command -v mise &> /dev/null; then - eval "$(mise activate bash)" -fi - -if command -v starship &> /dev/null; then - eval "$(starship init bash)" -fi - -if command -v zoxide &> /dev/null; then - eval "$(zoxide init bash)" -fi - -if command -v try &> /dev/null; then - eval "$(SHELL=/bin/bash command try init ~/Work/tries)" -fi - -if command -v fzf &> /dev/null; then - if [[ -f /usr/share/fzf/completion.bash ]]; then - source /usr/share/fzf/completion.bash - fi - if [[ -f /usr/share/fzf/key-bindings.bash ]]; then - source /usr/share/fzf/key-bindings.bash - fi -fi diff --git a/core/home/config/nomarchy/default/bash/inputrc b/core/home/config/nomarchy/default/bash/inputrc deleted file mode 100644 index 3b48e55..0000000 --- a/core/home/config/nomarchy/default/bash/inputrc +++ /dev/null @@ -1,47 +0,0 @@ -set meta-flag on -set input-meta on -set output-meta on -set convert-meta off -set completion-ignore-case on -set completion-prefix-display-length 2 -set show-all-if-ambiguous on -set show-all-if-unmodified on - -# Arrow keys match what you've typed so far against your command history -"\e[A": history-search-backward -"\e[B": history-search-forward -"\e[C": forward-char -"\e[D": backward-char - -# Immediately add a trailing slash when autocompleting symlinks to directories -set mark-symlinked-directories on - -# Do not autocomplete hidden files unless the pattern explicitly begins with a dot -set match-hidden-files off - -# Show all autocomplete results at once -set page-completions off - -# If there are more than 200 possible completions for a word, ask to show them all -set completion-query-items 200 - -# Show extra file information when completing, like `ls -F` does -set visible-stats on - -# Be more intelligent when autocompleting by also looking at the text after -# the cursor. For example, when the current line is "cd ~/src/mozil", and -# the cursor is on the "z", pressing Tab will not autocomplete it to "cd -# ~/src/mozillail", but to "cd ~/src/mozilla". (This is supported by the -# Readline used by Bash 4.) -set skip-completed-text on - -# Coloring for Bash 4 tab completions. -set colored-stats on - -# Cycle forward and backward through completion candidates (tab/shift+tab) -# (completion listing and display behavior configured above) -TAB: menu-complete -"\e[Z": menu-complete-backward - -# On first Tab, complete the common prefix before cycling candidates -set menu-complete-display-prefix on diff --git a/core/home/config/nomarchy/default/bash/rc b/core/home/config/nomarchy/default/bash/rc deleted file mode 100644 index 5d0a39e..0000000 --- a/core/home/config/nomarchy/default/bash/rc +++ /dev/null @@ -1,6 +0,0 @@ -source ~/.config/nomarchy/default/bash/envs -source ~/.config/nomarchy/default/bash/shell -source ~/.config/nomarchy/default/bash/aliases -source ~/.config/nomarchy/default/bash/functions -source ~/.config/nomarchy/default/bash/init -[[ $- == *i* ]] && bind -f ~/.config/nomarchy/default/bash/inputrc diff --git a/core/home/config/nomarchy/default/bash/shell b/core/home/config/nomarchy/default/bash/shell deleted file mode 100644 index 7eb4867..0000000 --- a/core/home/config/nomarchy/default/bash/shell +++ /dev/null @@ -1,13 +0,0 @@ -# History control -shopt -s histappend -HISTCONTROL=ignoreboth -HISTSIZE=32768 -HISTFILESIZE="${HISTSIZE}" - -# Autocompletion -if [[ ! -v BASH_COMPLETION_VERSINFO && -f /usr/share/bash-completion/bash_completion ]]; then - source /usr/share/bash-completion/bash_completion -fi - -# Ensure command hashing is off for mise -set +h diff --git a/core/home/config/nomarchy/default/chromium/extensions/copy-url/background.js b/core/home/config/nomarchy/default/chromium/extensions/copy-url/background.js deleted file mode 100644 index a2537c5..0000000 --- a/core/home/config/nomarchy/default/chromium/extensions/copy-url/background.js +++ /dev/null @@ -1,21 +0,0 @@ -chrome.commands.onCommand.addListener((command) => { - if (command === 'copy-url') { - chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { - const currentTab = tabs[0]; - - chrome.scripting.executeScript({ - target: { tabId: currentTab.id }, - func: () => { - navigator.clipboard.writeText(window.location.href); - } - }).then(() => { - chrome.notifications.create({ - type: 'basic', - iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', - title: ' URL copied to clipboard', - message: '' - }); - }); - }); - } -}); diff --git a/core/home/config/nomarchy/default/chromium/extensions/copy-url/icon.png b/core/home/config/nomarchy/default/chromium/extensions/copy-url/icon.png deleted file mode 120000 index e088259..0000000 --- a/core/home/config/nomarchy/default/chromium/extensions/copy-url/icon.png +++ /dev/null @@ -1 +0,0 @@ -../../../../icon.png \ No newline at end of file diff --git a/core/home/config/nomarchy/default/chromium/extensions/copy-url/manifest.json b/core/home/config/nomarchy/default/chromium/extensions/copy-url/manifest.json deleted file mode 100644 index 74b9004..0000000 --- a/core/home/config/nomarchy/default/chromium/extensions/copy-url/manifest.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "manifest_version": 3, - "name": "Copy URL", - "version": "1.0", - "description": "Copy current URL to clipboard, this extension is installed by Nomarchy", - "permissions": ["activeTab", "scripting", "notifications"], - "icons": { - "16": "icon.png", - "48": "icon.png", - "128": "icon.png" - }, - "commands": { - "copy-url": { - "suggested_key": {"default": "Alt+Shift+L"}, - "description": "Copy URL" - } - }, - "background": {"service_worker": "background.js"} -} diff --git a/core/home/config/nomarchy/default/ghostty/screensaver b/core/home/config/nomarchy/default/ghostty/screensaver deleted file mode 100644 index dc35906..0000000 --- a/core/home/config/nomarchy/default/ghostty/screensaver +++ /dev/null @@ -1,3 +0,0 @@ -window-padding-x = 0 -window-padding-y = 0 -window-padding-color = "extend-always" diff --git a/core/home/config/nomarchy/default/gpg/dirmngr.conf b/core/home/config/nomarchy/default/gpg/dirmngr.conf deleted file mode 100644 index 8d32790..0000000 --- a/core/home/config/nomarchy/default/gpg/dirmngr.conf +++ /dev/null @@ -1,7 +0,0 @@ -keyserver hkps://keyserver.ubuntu.com -keyserver hkps://pgp.surfnet.nl -keyserver hkps://keys.mailvelope.com -keyserver hkps://keyring.debian.org -keyserver hkps://pgp.mit.edu - -connect-quick-timeout 4 diff --git a/core/home/config/nomarchy/default/hypr/apps.conf b/core/home/config/nomarchy/default/hypr/apps.conf deleted file mode 100644 index 018bef9..0000000 --- a/core/home/config/nomarchy/default/hypr/apps.conf +++ /dev/null @@ -1,19 +0,0 @@ -# App-specific tweaks. All rules are class- or title-gated, so sourcing -# unconditionally is harmless when the app isn't installed or running. -source = ~/.config/nomarchy/default/hypr/apps/1password.conf -source = ~/.config/nomarchy/default/hypr/apps/bitwarden.conf -source = ~/.config/nomarchy/default/hypr/apps/browser.conf -source = ~/.config/nomarchy/default/hypr/apps/davinci-resolve.conf -source = ~/.config/nomarchy/default/hypr/apps/geforce.conf -source = ~/.config/nomarchy/default/hypr/apps/hyprshot.conf -source = ~/.config/nomarchy/default/hypr/apps/localsend.conf -source = ~/.config/nomarchy/default/hypr/apps/moonlight.conf -source = ~/.config/nomarchy/default/hypr/apps/pip.conf -source = ~/.config/nomarchy/default/hypr/apps/qemu.conf -source = ~/.config/nomarchy/default/hypr/apps/retroarch.conf -source = ~/.config/nomarchy/default/hypr/apps/steam.conf -source = ~/.config/nomarchy/default/hypr/apps/system.conf -source = ~/.config/nomarchy/default/hypr/apps/telegram.conf -source = ~/.config/nomarchy/default/hypr/apps/terminals.conf -source = ~/.config/nomarchy/default/hypr/apps/walker.conf -source = ~/.config/nomarchy/default/hypr/apps/webcam-overlay.conf diff --git a/core/home/config/nomarchy/default/hypr/apps/1password.conf b/core/home/config/nomarchy/default/hypr/apps/1password.conf deleted file mode 100644 index 9b4f4d1..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/1password.conf +++ /dev/null @@ -1,2 +0,0 @@ -windowrulev2 = noscreenshare, class:^(1[p|P]assword)$ -windowrulev2 = tag +floating-window, class:^(1[p|P]assword)$ diff --git a/core/home/config/nomarchy/default/hypr/apps/bitwarden.conf b/core/home/config/nomarchy/default/hypr/apps/bitwarden.conf deleted file mode 100644 index d77503f..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/bitwarden.conf +++ /dev/null @@ -1,6 +0,0 @@ -windowrulev2 = noscreenshare, class:^(Bitwarden)$ -windowrulev2 = tag +floating-window, class:^(Bitwarden)$ - -# Bitwarden Chrome Extension -windowrulev2 = noscreenshare, class:chrome-nngceckbapebfimnlniiiahkandclblb-Default -windowrulev2 = tag +floating-window, class:chrome-nngceckbapebfimnlniiiahkandclblb-Default diff --git a/core/home/config/nomarchy/default/hypr/apps/browser.conf b/core/home/config/nomarchy/default/hypr/apps/browser.conf deleted file mode 100644 index 3316288..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/browser.conf +++ /dev/null @@ -1,16 +0,0 @@ -# Browser types -windowrulev2 = tag +chromium-based-browser, class:((google-)?[cC]hrom(e|ium)|[bB]rave-browser|[mM]icrosoft-edge|Vivaldi-stable|helium) -windowrulev2 = tag +firefox-based-browser, class:([fF]irefox|zen|librewolf) -windowrulev2 = tag -default-opacity, tag:chromium-based-browser -windowrulev2 = tag -default-opacity, tag:firefox-based-browser - -# Video apps: remove chromium browser tag so they don't get opacity applied -windowrulev2 = tag -chromium-based-browser, class:(chrome-youtube.com__-Default|chrome-app.zoom.us__wc_home-Default) -windowrulev2 = tag -default-opacity, class:(chrome-youtube.com__-Default|chrome-app.zoom.us__wc_home-Default) - -# Force chromium-based browsers into a tile to deal with --app bug -windowrulev2 = tile, tag:chromium-based-browser - -# Only a subtle opacity change, but not for video sites -windowrulev2 = opacity 1.0 0.97, tag:chromium-based-browser -windowrulev2 = opacity 1.0 0.97, tag:firefox-based-browser diff --git a/core/home/config/nomarchy/default/hypr/apps/davinci-resolve.conf b/core/home/config/nomarchy/default/hypr/apps/davinci-resolve.conf deleted file mode 100644 index d019e9d..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/davinci-resolve.conf +++ /dev/null @@ -1,2 +0,0 @@ -# Focus floating DaVinci Resolve dialog windows -windowrulev2 = stayfocused, class:.*[Rr]esolve.*, float:1 diff --git a/core/home/config/nomarchy/default/hypr/apps/geforce.conf b/core/home/config/nomarchy/default/hypr/apps/geforce.conf deleted file mode 100644 index 5d47010..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/geforce.conf +++ /dev/null @@ -1 +0,0 @@ -windowrulev2 = idleinhibit fullscreen, class:^(GeForceNOW)$ diff --git a/core/home/config/nomarchy/default/hypr/apps/hyprshot.conf b/core/home/config/nomarchy/default/hypr/apps/hyprshot.conf deleted file mode 100644 index 5d0b7ba..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/hyprshot.conf +++ /dev/null @@ -1,2 +0,0 @@ -# Remove 1px border around hyprshot screenshots -layerrule = noanim, namespace:selection diff --git a/core/home/config/nomarchy/default/hypr/apps/localsend.conf b/core/home/config/nomarchy/default/hypr/apps/localsend.conf deleted file mode 100644 index a59d810..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/localsend.conf +++ /dev/null @@ -1,4 +0,0 @@ -# Float LocalSend and fzf file picker -windowrulev2 = float, class:(Share|localsend) -windowrulev2 = center, class:(Share|localsend) -windowrulev2 = size 1100 700, class:localsend diff --git a/core/home/config/nomarchy/default/hypr/apps/moonlight.conf b/core/home/config/nomarchy/default/hypr/apps/moonlight.conf deleted file mode 100644 index be31905..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/moonlight.conf +++ /dev/null @@ -1,2 +0,0 @@ -windowrulev2 = fullscreen, class:^(com.moonlight_stream.Moonlight)$ -windowrulev2 = idleinhibit fullscreen, class:^(com.moonlight_stream.Moonlight)$ diff --git a/core/home/config/nomarchy/default/hypr/apps/pip.conf b/core/home/config/nomarchy/default/hypr/apps/pip.conf deleted file mode 100644 index 21eb442..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/pip.conf +++ /dev/null @@ -1,10 +0,0 @@ -# Picture-in-picture overlays -windowrulev2 = tag +pip, title:(Picture.?in.?[Pp]icture) -windowrulev2 = tag -default-opacity, tag:pip -windowrulev2 = float, tag:pip -windowrulev2 = pin, tag:pip -windowrulev2 = size 600 338, tag:pip -windowrulev2 = keepaspectratio, tag:pip -windowrulev2 = bordersize 0, tag:pip -windowrulev2 = opacity 1 1, tag:pip -windowrulev2 = move (monitor_w-window_w-40) (monitor_h*0.04), tag:pip diff --git a/core/home/config/nomarchy/default/hypr/apps/qemu.conf b/core/home/config/nomarchy/default/hypr/apps/qemu.conf deleted file mode 100644 index 66598aa..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/qemu.conf +++ /dev/null @@ -1,2 +0,0 @@ -windowrulev2 = tag -default-opacity, class:qemu -windowrulev2 = opacity 1 1, class:qemu diff --git a/core/home/config/nomarchy/default/hypr/apps/retroarch.conf b/core/home/config/nomarchy/default/hypr/apps/retroarch.conf deleted file mode 100644 index d2df838..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/retroarch.conf +++ /dev/null @@ -1,4 +0,0 @@ -windowrulev2 = fullscreen, class:com.libretro.RetroArch -windowrulev2 = tag -default-opacity, class:com.libretro.RetroArch -windowrulev2 = opacity 1 1, class:com.libretro.RetroArch -windowrulev2 = idleinhibit fullscreen, class:com.libretro.RetroArch diff --git a/core/home/config/nomarchy/default/hypr/apps/steam.conf b/core/home/config/nomarchy/default/hypr/apps/steam.conf deleted file mode 100644 index f65a215..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/steam.conf +++ /dev/null @@ -1,8 +0,0 @@ -# Float Steam -windowrulev2 = float, class:steam -windowrulev2 = center, class:steam, title:Steam -windowrulev2 = tag -default-opacity, class:steam.* -windowrulev2 = opacity 1 1, class:steam.* -windowrulev2 = size 1100 700, class:steam, title:Steam -windowrulev2 = size 460 800, class:steam, title:Friends List -windowrulev2 = idleinhibit fullscreen, class:steam diff --git a/core/home/config/nomarchy/default/hypr/apps/system.conf b/core/home/config/nomarchy/default/hypr/apps/system.conf deleted file mode 100644 index aa67771..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/system.conf +++ /dev/null @@ -1,23 +0,0 @@ -# Floating windows -windowrulev2 = float, tag:floating-window -windowrulev2 = center, tag:floating-window -windowrulev2 = size 875 600, tag:floating-window - -windowrulev2 = tag +floating-window, class:(org.nomarchy.bluetui|org.nomarchy.impala|org.nomarchy.wiremix|org.nomarchy.btop|org.nomarchy.terminal|org.nomarchy.bash|org.gnome.NautilusPreviewer|org.gnome.Evince|com.gabm.satty|Nomarchy|About|TUI.float|imv|mpv) -windowrulev2 = tag +floating-window, class:(xdg-desktop-portal-gtk|sublime_text|DesktopEditors|org.gnome.Nautilus), title:^(Open.*Files?|Open [F|f]older.*|Save.*Files?|Save.*As|Save|All Files|.*wants to [open|save].*|[C|c]hoose.*) -windowrulev2 = float, class:org.gnome.Calculator - -# Fullscreen screensaver -windowrulev2 = fullscreen, class:org.nomarchy.screensaver -windowrulev2 = float, class:org.nomarchy.screensaver -windowrulev2 = animation slide, class:org.nomarchy.screensaver - -# No transparency on media windows -windowrulev2 = tag -default-opacity, class:^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$ -windowrulev2 = opacity 1 1, class:^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$ - -# Popped window rounding -windowrulev2 = rounding 8, tag:pop - -# Prevent idle while open -windowrulev2 = idleinhibit always, tag:noidle diff --git a/core/home/config/nomarchy/default/hypr/apps/telegram.conf b/core/home/config/nomarchy/default/hypr/apps/telegram.conf deleted file mode 100644 index 6ecea41..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/telegram.conf +++ /dev/null @@ -1,2 +0,0 @@ -# Prevent Telegram from stealing focus on new messages -windowrulev2 = focusonactivate off, class:org.telegram.desktop diff --git a/core/home/config/nomarchy/default/hypr/apps/terminals.conf b/core/home/config/nomarchy/default/hypr/apps/terminals.conf deleted file mode 100644 index 20b4f89..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/terminals.conf +++ /dev/null @@ -1,4 +0,0 @@ -# Define terminal tag to style them uniformly -windowrulev2 = tag +terminal, class:(Alacritty|kitty|com.mitchellh.ghostty) -windowrulev2 = tag -default-opacity, tag:terminal -windowrulev2 = opacity 0.97 0.9, tag:terminal diff --git a/core/home/config/nomarchy/default/hypr/apps/walker.conf b/core/home/config/nomarchy/default/hypr/apps/walker.conf deleted file mode 100644 index 1d2c2b3..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/walker.conf +++ /dev/null @@ -1,2 +0,0 @@ -# Application-specific animation -layerrule = noanim, namespace:walker diff --git a/core/home/config/nomarchy/default/hypr/apps/webcam-overlay.conf b/core/home/config/nomarchy/default/hypr/apps/webcam-overlay.conf deleted file mode 100644 index 459ceb6..0000000 --- a/core/home/config/nomarchy/default/hypr/apps/webcam-overlay.conf +++ /dev/null @@ -1,6 +0,0 @@ -# Webcam overlay for screen recording -windowrulev2 = float, title:WebcamOverlay -windowrulev2 = pin, title:WebcamOverlay -windowrulev2 = noinitialfocus, title:WebcamOverlay -windowrulev2 = nodim, title:WebcamOverlay -windowrulev2 = move (monitor_w-window_w-40) (monitor_h-window_h-40), title:WebcamOverlay diff --git a/core/home/config/nomarchy/default/hypr/autostart.conf b/core/home/config/nomarchy/default/hypr/autostart.conf deleted file mode 100644 index 704cb75..0000000 --- a/core/home/config/nomarchy/default/hypr/autostart.conf +++ /dev/null @@ -1,17 +0,0 @@ -exec-once = uwsm-app -- hypridle -exec-once = uwsm-app -- mako -exec-once = nomarchy-welcome -# exec-once = uwsm-app -- waybar -# fcitx5 is autostarted by NixOS's i18n.inputMethod when -# nomarchy.system.inputMethod.enable = true; no manual exec-once needed. -# swaybg is started as a systemd user service (nomarchy-wallpaper.service) -# so failures surface in logs and the wallpaper survives Hyprland restarts. -exec-once = uwsm-app -- swayosd-server -exec-once = nomarchy-on-boot - -# Slow app launch fix -- set systemd vars -exec-once = systemctl --user import-environment $(env | cut -d'=' -f 1) -exec-once = dbus-update-activation-environment --systemd --all - -# Network Manager -exec-once = uwsm-app 'nm-applet --indicator' diff --git a/core/home/config/nomarchy/default/hypr/bindings/clipboard.conf b/core/home/config/nomarchy/default/hypr/bindings/clipboard.conf deleted file mode 100644 index be3a6ab..0000000 --- a/core/home/config/nomarchy/default/hypr/bindings/clipboard.conf +++ /dev/null @@ -1,5 +0,0 @@ -# Copy / Paste -bindd = SUPER, C, Universal copy, sendshortcut, CTRL, Insert, -bindd = SUPER, V, Universal paste, sendshortcut, SHIFT, Insert, -bindd = SUPER, X, Universal cut, sendshortcut, CTRL, X, -bindd = SUPER CTRL, V, Clipboard manager, exec, nomarchy-launch-walker -m clipboard diff --git a/core/home/config/nomarchy/default/hypr/bindings/media.conf b/core/home/config/nomarchy/default/hypr/bindings/media.conf deleted file mode 100644 index c4d5514..0000000 --- a/core/home/config/nomarchy/default/hypr/bindings/media.conf +++ /dev/null @@ -1,28 +0,0 @@ -# Only display the OSD on the currently focused monitor -$osdclient = swayosd-client --monitor "$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name')" - -# Laptop multimedia keys for volume and LCD brightness (with OSD) -bindeld = ,XF86AudioRaiseVolume, Volume up, exec, $osdclient --output-volume raise -bindeld = ,XF86AudioLowerVolume, Volume down, exec, $osdclient --output-volume lower -bindeld = ,XF86AudioMute, Mute, exec, $osdclient --output-volume mute-toggle -bindeld = ,XF86AudioMicMute, Mute microphone, exec, $osdclient --input-volume mute-toggle -bindeld = ,XF86MonBrightnessUp, Brightness up, exec, nomarchy-brightness-display +5% -bindeld = ,XF86MonBrightnessDown, Brightness down, exec, nomarchy-brightness-display 5%- -bindeld = ,XF86KbdBrightnessUp, Keyboard brightness up, exec, nomarchy-brightness-keyboard up -bindeld = ,XF86KbdBrightnessDown, Keyboard brightness down, exec, nomarchy-brightness-keyboard down -bindld = ,XF86KbdLightOnOff, Keyboard backlight cycle, exec, nomarchy-brightness-keyboard cycle - -# Precise 1% multimedia adjustments with Alt modifier -bindeld = ALT, XF86AudioRaiseVolume, Volume up precise, exec, $osdclient --output-volume +1 -bindeld = ALT, XF86AudioLowerVolume, Volume down precise, exec, $osdclient --output-volume -1 -bindeld = ALT, XF86MonBrightnessUp, Brightness up precise, exec, nomarchy-brightness-display +1% -bindeld = ALT, XF86MonBrightnessDown, Brightness down precise, exec, nomarchy-brightness-display 1%- - -# Requires playerctl -bindld = , XF86AudioNext, Next track, exec, $osdclient --playerctl next -bindld = , XF86AudioPause, Pause, exec, $osdclient --playerctl play-pause -bindld = , XF86AudioPlay, Play, exec, $osdclient --playerctl play-pause -bindld = , XF86AudioPrev, Previous track, exec, $osdclient --playerctl previous - -# Switch audio output with Super + Mute -bindld = SUPER, XF86AudioMute, Switch audio output, exec, nomarchy-cmd-audio-switch diff --git a/core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf b/core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf deleted file mode 100644 index 7788a5b..0000000 --- a/core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf +++ /dev/null @@ -1,128 +0,0 @@ -# Close windows -bindd = SUPER, W, Close window, killactive, -bindd = CTRL ALT, DELETE, Close all windows, exec, nomarchy-hyprland-window-close-all - -# Control tiling -bindd = SUPER, J, Toggle window split, layoutmsg, togglesplit -bindd = SUPER, P, Pseudo window, pseudo, # dwindle -bindd = SUPER, T, Toggle window floating/tiling, togglefloating, -bindd = SUPER, F, Full screen, fullscreen, 0 -bindd = SUPER CTRL, F, Tiled full screen, fullscreenstate, 0 2 -bindd = SUPER ALT, F, Full width, fullscreen, 1 -bindd = SUPER, O, Pop window out (float & pin), exec, nomarchy-hyprland-window-pop -bindd = SUPER, L, Toggle workspace layout, exec, nomarchy-hyprland-workspace-layout-toggle - -# Move focus with SUPER + arrow keys -bindd = SUPER, LEFT, Move window focus left, movefocus, l -bindd = SUPER, RIGHT, Move window focus right, movefocus, r -bindd = SUPER, UP, Move window focus up, movefocus, u -bindd = SUPER, DOWN, Move window focus down, movefocus, d - -# Switch workspaces with SUPER + [1-9; 0] -bindd = SUPER, code:10, Switch to workspace 1, workspace, 1 -bindd = SUPER, code:11, Switch to workspace 2, workspace, 2 -bindd = SUPER, code:12, Switch to workspace 3, workspace, 3 -bindd = SUPER, code:13, Switch to workspace 4, workspace, 4 -bindd = SUPER, code:14, Switch to workspace 5, workspace, 5 -bindd = SUPER, code:15, Switch to workspace 6, workspace, 6 -bindd = SUPER, code:16, Switch to workspace 7, workspace, 7 -bindd = SUPER, code:17, Switch to workspace 8, workspace, 8 -bindd = SUPER, code:18, Switch to workspace 9, workspace, 9 -bindd = SUPER, code:19, Switch to workspace 10, workspace, 10 - -# Move active window to a workspace with SUPER + SHIFT + [1-9; 0] -bindd = SUPER SHIFT, code:10, Move window to workspace 1, movetoworkspace, 1 -bindd = SUPER SHIFT, code:11, Move window to workspace 2, movetoworkspace, 2 -bindd = SUPER SHIFT, code:12, Move window to workspace 3, movetoworkspace, 3 -bindd = SUPER SHIFT, code:13, Move window to workspace 4, movetoworkspace, 4 -bindd = SUPER SHIFT, code:14, Move window to workspace 5, movetoworkspace, 5 -bindd = SUPER SHIFT, code:15, Move window to workspace 6, movetoworkspace, 6 -bindd = SUPER SHIFT, code:16, Move window to workspace 7, movetoworkspace, 7 -bindd = SUPER SHIFT, code:17, Move window to workspace 8, movetoworkspace, 8 -bindd = SUPER SHIFT, code:18, Move window to workspace 9, movetoworkspace, 9 -bindd = SUPER SHIFT, code:19, Move window to workspace 10, movetoworkspace, 10 - -# Move active window silently to a workspace with SUPER + SHIFT + ALT + [1-9; 0] -bindd = SUPER SHIFT ALT, code:10, Move window silently to workspace 1, movetoworkspacesilent, 1 -bindd = SUPER SHIFT ALT, code:11, Move window silently to workspace 2, movetoworkspacesilent, 2 -bindd = SUPER SHIFT ALT, code:12, Move window silently to workspace 3, movetoworkspacesilent, 3 -bindd = SUPER SHIFT ALT, code:13, Move window silently to workspace 4, movetoworkspacesilent, 4 -bindd = SUPER SHIFT ALT, code:14, Move window silently to workspace 5, movetoworkspacesilent, 5 -bindd = SUPER SHIFT ALT, code:15, Move window silently to workspace 6, movetoworkspacesilent, 6 -bindd = SUPER SHIFT ALT, code:16, Move window silently to workspace 7, movetoworkspacesilent, 7 -bindd = SUPER SHIFT ALT, code:17, Move window silently to workspace 8, movetoworkspacesilent, 8 -bindd = SUPER SHIFT ALT, code:18, Move window silently to workspace 9, movetoworkspacesilent, 9 -bindd = SUPER SHIFT ALT, code:19, Move window silently to workspace 10, movetoworkspacesilent, 10 - -# Control scratchpad -bindd = SUPER, S, Toggle scratchpad, togglespecialworkspace, scratchpad -bindd = SUPER ALT, S, Move window to scratchpad, movetoworkspacesilent, special:scratchpad - -# TAB between workspaces -bindd = SUPER, TAB, Next workspace, workspace, e+1 -bindd = SUPER SHIFT, TAB, Previous workspace, workspace, e-1 -bindd = SUPER CTRL, TAB, Former workspace, workspace, previous - -# Move workspaces to other monitors -bindd = SUPER SHIFT ALT, LEFT, Move workspace to left monitor, movecurrentworkspacetomonitor, l -bindd = SUPER SHIFT ALT, RIGHT, Move workspace to right monitor, movecurrentworkspacetomonitor, r -bindd = SUPER SHIFT ALT, UP, Move workspace to up monitor, movecurrentworkspacetomonitor, u -bindd = SUPER SHIFT ALT, DOWN, Move workspace to down monitor, movecurrentworkspacetomonitor, d - -# Swap active window with the one next to it with SUPER + SHIFT + arrow keys -bindd = SUPER SHIFT, LEFT, Swap window to the left, swapwindow, l -bindd = SUPER SHIFT, RIGHT, Swap window to the right, swapwindow, r -bindd = SUPER SHIFT, UP, Swap window up, swapwindow, u -bindd = SUPER SHIFT, DOWN, Swap window down, swapwindow, d - -# Cycle through applications on active workspace -bindd = ALT, TAB, Cycle to next window, cyclenext -bindd = ALT SHIFT, TAB, Cycle to prev window, cyclenext, prev -bindd = ALT, TAB, Reveal active window on top, bringactivetotop -bindd = ALT SHIFT, TAB, Reveal active window on top, bringactivetotop - -# Resize active window -bindd = SUPER, code:20, Expand window left, resizeactive, -100 0 # - key -bindd = SUPER, code:21, Shrink window left, resizeactive, 100 0 # = key -bindd = SUPER SHIFT, code:20, Shrink window up, resizeactive, 0 -100 -bindd = SUPER SHIFT, code:21, Expand window down, resizeactive, 0 100 - -# Scroll through existing workspaces with SUPER + scroll -bindd = SUPER, mouse_down, Scroll active workspace forward, workspace, e+1 -bindd = SUPER, mouse_up, Scroll active workspace backward, workspace, e-1 - -# Move/resize windows with mainMod + LMB/RMB and dragging -bindmd = SUPER, mouse:272, Move window, movewindow -bindmd = SUPER, mouse:273, Resize window, resizewindow - -# Toggle groups -bindd = SUPER, G, Toggle window grouping, togglegroup -bindd = SUPER ALT, G, Move active window out of group, moveoutofgroup - -# Join groups -bindd = SUPER ALT, LEFT, Move window to group on left, moveintogroup, l -bindd = SUPER ALT, RIGHT, Move window to group on right, moveintogroup, r -bindd = SUPER ALT, UP, Move window to group on top, moveintogroup, u -bindd = SUPER ALT, DOWN, Move window to group on bottom, moveintogroup, d - -# Navigate a single set of grouped windows -bindd = SUPER ALT, TAB, Next window in group, changegroupactive, f -bindd = SUPER ALT SHIFT, TAB, Previous window in group, changegroupactive, b - -# Window navigation for grouped windows -bindd = SUPER CTRL, LEFT, Move grouped window focus left, changegroupactive, b -bindd = SUPER CTRL, RIGHT, Move grouped window focus right, changegroupactive, f - -# Scroll through a set of grouped windows with SUPER + ALT + scroll -bindd = SUPER ALT, mouse_down, Next window in group, changegroupactive, f -bindd = SUPER ALT, mouse_up, Previous window in group, changegroupactive, b - -# Activate window in a group by number -bindd = SUPER ALT, code:10, Switch to group window 1, changegroupactive, 1 -bindd = SUPER ALT, code:11, Switch to group window 2, changegroupactive, 2 -bindd = SUPER ALT, code:12, Switch to group window 3, changegroupactive, 3 -bindd = SUPER ALT, code:13, Switch to group window 4, changegroupactive, 4 -bindd = SUPER ALT, code:14, Switch to group window 5, changegroupactive, 5 - -# Cycle monitor scaling -bindd = SUPER, Slash, Cycle monitor scaling, exec, nomarchy-hyprland-monitor-scaling-cycle diff --git a/core/home/config/nomarchy/default/hypr/bindings/utilities.conf b/core/home/config/nomarchy/default/hypr/bindings/utilities.conf deleted file mode 100644 index 445c370..0000000 --- a/core/home/config/nomarchy/default/hypr/bindings/utilities.conf +++ /dev/null @@ -1,59 +0,0 @@ -# Menus -bindd = SUPER, SPACE, Launch apps, exec, nomarchy-launch-walker -bindd = SUPER CTRL, E, Emoji picker, exec, nomarchy-menu symbols -bindd = SUPER CTRL, C, Capture menu, exec, nomarchy-menu capture -bindd = SUPER CTRL, O, Toggle menu, exec, nomarchy-menu toggle -bindd = SUPER ALT, SPACE, Toggle top bar, exec, nomarchy-toggle-waybar -bindd = SUPER, ESCAPE, System menu, exec, nomarchy-menu system -bindld = , XF86PowerOff, Power menu, exec, nomarchy-menu system -bindd = SUPER, K, Show key bindings, exec, nomarchy-menu-keybindings -bindd = , XF86Calculator, Calculator, exec, gnome-calculator - -# Aesthetics -bindd = SUPER SHIFT, SPACE, Nomarchy menu, exec, nomarchy-menu -bindd = SUPER CTRL, SPACE, Theme background menu, exec, nomarchy-menu background -bindd = SUPER SHIFT CTRL, SPACE, Theme menu, exec, nomarchy-menu theme -bindd = SUPER, BACKSPACE, Toggle window transparency, exec, nomarchy-hyprland-active-window-transparency-toggle -bindd = SUPER SHIFT, BACKSPACE, Toggle window gaps, exec, nomarchy-hyprland-window-gaps-toggle -bindd = SUPER CTRL, BACKSPACE, Toggle single-window square aspect, exec, nomarchy-hyprland-window-single-square-aspect-toggle - -# Notifications -bindd = SUPER, COMMA, Dismiss last notification, exec, makoctl dismiss -bindd = SUPER SHIFT, COMMA, Dismiss all notifications, exec, makoctl dismiss --all -bindd = SUPER CTRL, COMMA, Toggle silencing notifications, exec, nomarchy-toggle-notification-silencing -bindd = SUPER ALT, COMMA, Invoke last notification, exec, makoctl invoke -bindd = SUPER SHIFT ALT, COMMA, Restore last notification, exec, makoctl restore - -# Toggles -bindd = SUPER CTRL, I, Toggle locking on idle, exec, nomarchy-toggle-idle -bindd = SUPER CTRL, N, Toggle nightlight, exec, nomarchy-toggle-nightlight - -# Control Apple Display brightness -bindd = CTRL, F1, Apple Display brightness down, exec, nomarchy-brightness-display-apple -5000 -bindd = CTRL, F2, Apple Display brightness up, exec, nomarchy-brightness-display-apple +5000 -bindd = SHIFT CTRL, F2, Apple Display full brightness, exec, nomarchy-brightness-display-apple +60000 - -# Captures -bindd = , PRINT, Screenshot, exec, nomarchy-cmd-screenshot -bindd = ALT, PRINT, Screenrecording, exec, nomarchy-menu screenrecord -bindd = SUPER, PRINT, Color picker, exec, pkill hyprpicker || hyprpicker -a - -# File sharing -bindd = SUPER CTRL, S, Share, exec, nomarchy-menu share - -# Waybar-less information -bindd = SUPER CTRL ALT, T, Show time, exec, notify-send -u low " $(date +"%A %H:%M · %d %B %Y · Week %V")" -bindd = SUPER CTRL ALT, B, Show battery remaining, exec, notify-send -u low "$(nomarchy-battery-status)" - -# Control panels -bindd = SUPER CTRL, A, Audio controls, exec, nomarchy-launch-audio -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 - -# 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 - -# Lock system -bindd = SUPER CTRL, L, Lock system, exec, nomarchy-lock-screen diff --git a/core/home/config/nomarchy/default/hypr/colors.conf b/core/home/config/nomarchy/default/hypr/colors.conf deleted file mode 100644 index e296abf..0000000 --- a/core/home/config/nomarchy/default/hypr/colors.conf +++ /dev/null @@ -1,12 +0,0 @@ -# Hyprland Color Configuration (Visual) -# This file contains only color-related settings. -# It's sourced AFTER theme colors are applied, allowing theme-specific overrides. - -# These are placeholder values - actual colors come from the active theme -# via ~/.config/nomarchy/current/theme/hyprland.conf - -# Border colors (set by theme-loader based on active theme) -# The theme's hyprland.conf will define $activeBorderColor and $inactiveBorderColor - -# Group bar colors (set by theme) -# Themes can override group bar colors in their hyprland.conf diff --git a/core/home/config/nomarchy/default/hypr/envs.conf b/core/home/config/nomarchy/default/hypr/envs.conf deleted file mode 100644 index 85afc77..0000000 --- a/core/home/config/nomarchy/default/hypr/envs.conf +++ /dev/null @@ -1,29 +0,0 @@ -# Cursor size -env = XCURSOR_SIZE,24 -env = HYPRCURSOR_SIZE,24 - -# Force all apps to use Wayland -env = GDK_BACKEND,wayland,x11,* -env = QT_QPA_PLATFORM,wayland;xcb -env = QT_STYLE_OVERRIDE,kvantum -env = SDL_VIDEODRIVER,wayland,x11 -env = MOZ_ENABLE_WAYLAND,1 -env = ELECTRON_OZONE_PLATFORM_HINT,wayland -env = OZONE_PLATFORM,wayland -env = XDG_SESSION_TYPE,wayland - -# Allow better support for screen sharing (Google Meet, Discord, etc) -env = XDG_CURRENT_DESKTOP,Hyprland -env = XDG_SESSION_DESKTOP,Hyprland - -xwayland { - force_zero_scaling = true -} - -# Use XCompose file -env = XCOMPOSEFILE,~/.XCompose - -# Don't show update on first launch -ecosystem { - no_update_news = true -} diff --git a/core/home/config/nomarchy/default/hypr/looknfeel.conf b/core/home/config/nomarchy/default/hypr/looknfeel.conf deleted file mode 100644 index 80ab2ca..0000000 --- a/core/home/config/nomarchy/default/hypr/looknfeel.conf +++ /dev/null @@ -1,146 +0,0 @@ -# Refer to https://wiki.hyprland.org/Configuring/Variables/ - -# Variables -$activeBorderColor = rgba(33ccffee) rgba(00ff99ee) 45deg -$inactiveBorderColor = rgba(595959aa) - -# https://wiki.hyprland.org/Configuring/Variables/#general -general { - gaps_in = 5 - gaps_out = 10 - - border_size = 2 - - # https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors - col.active_border = $activeBorderColor - col.inactive_border = $inactiveBorderColor - - # Set to true enable resizing windows by clicking and dragging on borders and gaps - resize_on_border = false - - # Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on - allow_tearing = false - - layout = dwindle -} - -# https://wiki.hyprland.org/Configuring/Variables/#decoration -decoration { - rounding = 0 - - shadow { - enabled = true - range = 2 - render_power = 3 - color = rgba(1a1a1aee) - } - - # https://wiki.hyprland.org/Configuring/Variables/#blur - blur { - enabled = true - size = 2 - passes = 2 - special = true - brightness = 0.60 - contrast = 0.75 - } -} - -# https://wiki.hypr.land/Configuring/Variables/#group -group { - col.border_active = $activeBorderColor - col.border_inactive = $inactiveBorderColor - col.border_locked_active = -1 - col.border_locked_inactive = -1 - - groupbar { - font_size = 12 - font_family = monospace - font_weight_active = ultraheavy - font_weight_inactive = normal - - indicator_height = 0 - indicator_gap = 5 - height = 22 - gaps_in = 5 - gaps_out = 0 - - text_color = rgb(ffffff) - text_color_inactive = rgba(ffffff90) - col.active = rgba(00000040) - col.inactive = rgba(00000020) - - gradients = true - gradient_rounding = 0 - gradient_round_only_edges = false - } -} - - -# https://wiki.hyprland.org/Configuring/Variables/#animations -animations { - enabled = yes - - # Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more - - bezier = easeOutQuint,0.23,1,0.32,1 - bezier = easeInOutCubic,0.65,0.05,0.36,1 - bezier = linear,0,0,1,1 - bezier = almostLinear,0.5,0.5,0.75,1.0 - bezier = quick,0.15,0,0.1,1 - - animation = global, 1, 10, default - animation = border, 1, 5.39, easeOutQuint - animation = windows, 1, 4.79, easeOutQuint - animation = windowsIn, 1, 4.1, easeOutQuint, popin 87% - animation = windowsOut, 1, 1.49, linear, popin 87% - animation = fadeIn, 1, 1.73, almostLinear - animation = fadeOut, 1, 1.46, almostLinear - animation = fade, 1, 3.03, quick - animation = layers, 1, 3.81, easeOutQuint - animation = layersIn, 1, 4, easeOutQuint, fade - animation = layersOut, 1, 1.5, linear, fade - animation = fadeLayersIn, 1, 1.79, almostLinear - animation = fadeLayersOut, 1, 1.39, almostLinear - animation = workspaces, 0, 0, ease - animation = specialWorkspace, 1, 4, easeOutQuint, slidevert -} - -# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more -dwindle { - pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below - preserve_split = true # You probably want this - force_split = 2 # Always split on the right -} - -# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more -master { - new_status = master -} - -# https://wiki.hyprland.org/Configuring/Variables/#misc -misc { - disable_hyprland_logo = true - disable_splash_rendering = true - disable_scale_notification = true - focus_on_activate = true -} - -# https://wiki.hypr.land/Configuring/Variables/#cursor -cursor { - hide_on_key_press = true - warp_on_change_workspace = 1 -} - -# Auto toggle scratchpad on switching workspace from scratchpad -binds { - hide_special_on_workspace_change = true -} - -# Style Gum confirm to match terminal theme -env = GUM_CONFIRM_PROMPT_FOREGROUND,6 # Cyan -env = GUM_CONFIRM_SELECTED_FOREGROUND,0 # Black -env = GUM_CONFIRM_SELECTED_BACKGROUND,2 # Green -env = GUM_CONFIRM_UNSELECTED_FOREGROUND,7 # White -env = GUM_CONFIRM_UNSELECTED_BACKGROUND,8 # Dark grey - # Dark grey diff --git a/core/home/config/nomarchy/default/hypr/windows.conf b/core/home/config/nomarchy/default/hypr/windows.conf deleted file mode 100644 index 64715d2..0000000 --- a/core/home/config/nomarchy/default/hypr/windows.conf +++ /dev/null @@ -1,15 +0,0 @@ -# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more -# Hyprland 0.53+ syntax -#windowrulev2 = suppressevent maximize, class:.* - -# Tag all windows for default opacity (apps can override with -default-opacity tag) -#windowrulev2 = tag +default-opacity, class:.* - -# Fix some dragging issues with XWayland -#windowrulev2 = nofocus, class:^$, title:^$, xwayland:1, float:1, fullscreen:0, pin:0 - -# App-specific tweaks (may remove default-opacity tag) -source = ~/.config/nomarchy/default/hypr/apps.conf - -# Apply default opacity after apps have had a chance to opt out -#windowrulev2 = opacity 0.97 0.9, tag:default-opacity diff --git a/core/home/config/nomarchy/default/mako/core.ini b/core/home/config/nomarchy/default/mako/core.ini deleted file mode 100644 index 8d38674..0000000 --- a/core/home/config/nomarchy/default/mako/core.ini +++ /dev/null @@ -1,37 +0,0 @@ -anchor=top-right -default-timeout=5000 -width=420 -outer-margin=20 -padding=10,15 -border-size=2 -max-icon-size=32 -font=sans-serif 14px - -[app-name=Spotify] -invisible=1 - -[mode=do-not-disturb] -invisible=true - -[mode=do-not-disturb app-name=notify-send] -invisible=false - -[urgency=critical] -default-timeout=0 -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 -format=<b>%s</b>\n%b diff --git a/core/home/config/nomarchy/default/walker/restart.conf b/core/home/config/nomarchy/default/walker/restart.conf deleted file mode 100644 index 5794031..0000000 --- a/core/home/config/nomarchy/default/walker/restart.conf +++ /dev/null @@ -1,3 +0,0 @@ -[Service] -Restart=always -RestartSec=2 diff --git a/core/home/config/nomarchy/default/walker/themes/nomarchy-default/layout.xml b/core/home/config/nomarchy/default/walker/themes/nomarchy-default/layout.xml deleted file mode 100644 index 3b7bda7..0000000 --- a/core/home/config/nomarchy/default/walker/themes/nomarchy-default/layout.xml +++ /dev/null @@ -1,156 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk" version="4.0"></requires> - <object class="GtkWindow" id="Window"> - <style> - <class name="window"></class> - </style> - <property name="resizable">true</property> - <property name="title">Walker</property> - <child> - <object class="GtkBox" id="BoxWrapper"> - <style> - <class name="box-wrapper"></class> - </style> - <property name="width-request">644</property> - <property name="overflow">hidden</property> - <property name="orientation">horizontal</property> - <property name="valign">center</property> - <property name="halign">center</property> - <child> - <object class="GtkBox" id="Box"> - <style> - <class name="box"></class> - </style> - <property name="orientation">vertical</property> - <property name="hexpand-set">true</property> - <property name="hexpand">true</property> - <property name="spacing">10</property> - <child> - <object class="GtkBox" id="SearchContainer"> - <style> - <class name="search-container"></class> - </style> - <property name="overflow">hidden</property> - <property name="orientation">horizontal</property> - <property name="halign">fill</property> - <property name="hexpand-set">true</property> - <property name="hexpand">true</property> - <child> - <object class="GtkEntry" id="Input"> - <style> - <class name="input"></class> - </style> - <property name="halign">fill</property> - <property name="hexpand-set">true</property> - <property name="hexpand">true</property> - </object> - </child> - </object> - </child> - <child> - <object class="GtkBox" id="ContentContainer"> - <style> - <class name="content-container"></class> - </style> - <property name="orientation">horizontal</property> - <property name="spacing">10</property> - <property name="vexpand">true</property> - <property name="vexpand-set">true</property> - <child> - <object class="GtkLabel" id="ElephantHint"> - <style> - <class name="elephant-hint"></class> - </style> - <property name="hexpand">true</property> - <property name="height-request">100</property> - <property name="label">Waiting for elephant...</property> - </object> - </child> - <child> - <object class="GtkLabel" id="Placeholder"> - <style> - <class name="placeholder"></class> - </style> - <property name="label">No Results</property> - <property name="yalign">0.0</property> - <property name="hexpand">true</property> - </object> - </child> - <child> - <object class="GtkScrolledWindow" id="Scroll"> - <style> - <class name="scroll"></class> - </style> - <property name="hexpand">true</property> - <property name="can_focus">false</property> - <property name="overlay-scrolling">true</property> - <property name="max-content-width">600</property> - <property name="max-content-height">300</property> - <property name="min-content-height">0</property> - <property name="propagate-natural-height">true</property> - <property name="propagate-natural-width">true</property> - <property name="hscrollbar-policy">automatic</property> - <property name="vscrollbar-policy">automatic</property> - <child> - <object class="GtkGridView" id="List"> - <style> - <class name="list"></class> - </style> - <property name="max_columns">1</property> - <property name="can_focus">false</property> - </object> - </child> - </object> - </child> - <child> - <object class="GtkBox" id="Preview"> - <style> - <class name="preview"></class> - </style> - </object> - </child> - </object> - </child> - <child> - <object class="GtkBox" id="Keybinds"> - <property name="hexpand">true</property> - <property name="margin-top">10</property> - <style> - <class name="keybinds"></class> - </style> - <child> - <object class="GtkBox" id="GlobalKeybinds"> - <property name="spacing">10</property> - <style> - <class name="global-keybinds"></class> - </style> - </object> - </child> - <child> - <object class="GtkBox" id="ItemKeybinds"> - <property name="hexpand">true</property> - <property name="halign">end</property> - <property name="spacing">10</property> - <style> - <class name="item-keybinds"></class> - </style> - </object> - </child> - </object> - </child> - <child> - <object class="GtkLabel" id="Error"> - <style> - <class name="error"></class> - </style> - <property name="xalign">0</property> - <property name="visible">false</property> - </object> - </child> - </object> - </child> - </object> - </child> - </object> -</interface> diff --git a/core/home/config/nomarchy/default/walker/themes/nomarchy-default/style.css b/core/home/config/nomarchy/default/walker/themes/nomarchy-default/style.css deleted file mode 100644 index 1eed746..0000000 --- a/core/home/config/nomarchy/default/walker/themes/nomarchy-default/style.css +++ /dev/null @@ -1,117 +0,0 @@ -@import "../../../../../../../.config/nomarchy/current/theme/walker.css"; - -* { - all: unset; -} - -* { - font-family: monospace; - font-size: 18px; - color: @text; -} - -scrollbar { - opacity: 0; -} - -.normal-icons { - -gtk-icon-size: 16px; -} - -.large-icons { - -gtk-icon-size: 32px; -} - -.box-wrapper { - background: alpha(@base, 0.95); - padding: 20px; - border: 2px solid @border; -} - -.preview-box { -} - -.box { -} - -.search-container { - background: @base; - padding: 10px; -} - -.input placeholder { - opacity: 0.5; -} - -.input { -} - -.input:focus, -.input:active { - box-shadow: none; - outline: none; -} - -.content-container { -} - -.placeholder { -} - -.scroll { -} - -.list { -} - -child, -child > * { -} - -child:hover .item-box { -} - -child:selected .item-box { - background-color: @selected-background; -} - -child:selected .item-box * { - color: @selected-text; -} - -.item-box { - padding-left: 14px; -} - -.item-text-box { - all: unset; - padding: 14px 0; -} - -.item-text { -} - -.item-subtext { - font-size: 0px; - min-height: 0px; - margin: 0px; - padding: 0px; -} - -.item-image { - margin-right: 14px; - -gtk-icon-transform: scale(0.9); -} - -.current { - font-style: italic; -} - -.keybind-hints { - background: @background; - padding: 10px; - margin-top: 10px; -} - -.preview { -} diff --git a/core/home/config/nomarchy/default/walker/walker.desktop b/core/home/config/nomarchy/default/walker/walker.desktop deleted file mode 100644 index fa7bdfe..0000000 --- a/core/home/config/nomarchy/default/walker/walker.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Name=Walker -Comment=Walker Service -Exec=walker --gapplication-service -StartupNotify=false -Terminal=false -Type=Application diff --git a/core/home/config/nomarchy/default/waybar/indicators/idle.sh b/core/home/config/nomarchy/default/waybar/indicators/idle.sh deleted file mode 100755 index a110cc4..0000000 --- a/core/home/config/nomarchy/default/waybar/indicators/idle.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -if pgrep -x hypridle >/dev/null; then - echo '{"text": ""}' -else - echo '{"text": "󱫖", "tooltip": "Idle lock disabled", "class": "active"}' -fi diff --git a/core/home/config/nomarchy/default/waybar/indicators/notification-silencing.sh b/core/home/config/nomarchy/default/waybar/indicators/notification-silencing.sh deleted file mode 100755 index 6bebb47..0000000 --- a/core/home/config/nomarchy/default/waybar/indicators/notification-silencing.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -if makoctl mode | grep -q 'do-not-disturb'; then - echo '{"text": "󰂛", "tooltip": "Notifications silenced", "class": "active"}' -else - echo '{"text": ""}' -fi diff --git a/core/home/config/nomarchy/default/waybar/indicators/screen-recording.sh b/core/home/config/nomarchy/default/waybar/indicators/screen-recording.sh deleted file mode 100755 index 567f386..0000000 --- a/core/home/config/nomarchy/default/waybar/indicators/screen-recording.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -if pgrep -f "^gpu-screen-recorder" >/dev/null; then - echo '{"text": "󰻂", "tooltip": "Stop recording", "class": "active"}' -else - echo '{"text": ""}' -fi diff --git a/core/home/config/nomarchy/default/wireplumber/wireplumber.conf.d/alsa-soft-mixer.conf b/core/home/config/nomarchy/default/wireplumber/wireplumber.conf.d/alsa-soft-mixer.conf deleted file mode 100644 index 6fc1dbd..0000000 --- a/core/home/config/nomarchy/default/wireplumber/wireplumber.conf.d/alsa-soft-mixer.conf +++ /dev/null @@ -1,18 +0,0 @@ -## Use software volume control for all ALSA devices. -## This prevents hardware mixer quirks (like muffled audio on Realtek codecs) -## and provides consistent volume behavior across all hardware. - -monitor.alsa.rules = [ - { - matches = [ - { - device.name = "~alsa_card.*" - } - ] - actions = { - update-props = { - api.alsa.soft-mixer = true - } - } - } -] diff --git a/core/home/config/nomarchy/default/xcompose b/core/home/config/nomarchy/default/xcompose deleted file mode 100644 index a45c5be..0000000 --- a/core/home/config/nomarchy/default/xcompose +++ /dev/null @@ -1,29 +0,0 @@ -include "%L" - -# Emoji -<Multi_key> <m> <s> : "😄" # smile -<Multi_key> <m> <c> : "😂" # cry -<Multi_key> <m> <l> : "😍" # love -<Multi_key> <m> <v> : "✌️" # victory -<Multi_key> <m> <h> : "❤️" # heart -<Multi_key> <m> <y> : "👍" # yes -<Multi_key> <m> <n> : "👎" # no -<Multi_key> <m> <f> : "🖕" # fuck -<Multi_key> <m> <w> : "🤞" # wish -<Multi_key> <m> <r> : "🤘" # rock -<Multi_key> <m> <k> : "😘" # kiss -<Multi_key> <m> <e> : "🙄" # eyeroll -<Multi_key> <m> <d> : "🤤" # droll -<Multi_key> <m> <m> : "💰" # money -<Multi_key> <m> <x> : "🎉" # xellebrate -<Multi_key> <m> <1> : "💯" # 100% -<Multi_key> <m> <t> : "🥂" # toast -<Multi_key> <m> <p> : "🙏" # pray -<Multi_key> <m> <i> : "😉" # wink -<Multi_key> <m> <o> : "👌" # OK -<Multi_key> <m> <g> : "👋" # greeting -<Multi_key> <m> <a> : "💪" # arm -<Multi_key> <m> <b> : "🤯" # blowing - -# Typography -<Multi_key> <space> <space> : "—" diff --git a/core/home/config/nomarchy/extensions/menu.sh b/core/home/config/nomarchy/extensions/menu.sh deleted file mode 100644 index 7afbae2..0000000 --- a/core/home/config/nomarchy/extensions/menu.sh +++ /dev/null @@ -1,20 +0,0 @@ -# Overwrite parts of the nomarchy-menu with user-specific submenus. -# See $OMARCHY_PATH/bin/nomarchy-menu for functions that can be overwritten. -# -# WARNING: Overwritten functions will obviously not be updated when Nomarchy changes. -# -# Example of minimal system menu: -# -# show_system_menu() { -# case $(menu "System" " Lock\n󰐥 Shutdown") in -# *Lock*) nomarchy-lock-screen ;; -# *Shutdown*) nomarchy-system-shutdown ;; -# *) back_to show_main_menu ;; -# esac -# } -# -# Example of overriding just the about menu action: (Using zsh instead of bash (default)) -# -# show_about() { -# exec nomarchy-launch-or-focus-tui "zsh -c 'fastfetch; read -k 1'" -# } diff --git a/core/home/config/nomarchy/hooks/battery-low.sample b/core/home/config/nomarchy/hooks/battery-low.sample deleted file mode 100644 index c1c1296..0000000 --- a/core/home/config/nomarchy/hooks/battery-low.sample +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# This hook is called with the current battery percentage when the low battery -# notification is sent. To put it into use, remove .sample from the name. - -SOUND_FILE="/usr/share/sounds/freedesktop/stereo/dialog-warning.oga" - -if nomarchy-cmd-present mpv && [[ -f $SOUND_FILE ]]; then - mpv --no-video "$SOUND_FILE" >/dev/null 2>&1 -fi diff --git a/core/home/config/nomarchy/hooks/font-set.sample b/core/home/config/nomarchy/hooks/font-set.sample deleted file mode 100644 index 3f0109a..0000000 --- a/core/home/config/nomarchy/hooks/font-set.sample +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# This hook is called with the snake-cased name of the font that has just been set. -# To put it into use, remove .sample from the name. - -# Example: Show the name of the theme that was just set. -# notify-send -u low "New font" "Your new font is $1" diff --git a/core/home/config/nomarchy/hooks/post-install.sample b/core/home/config/nomarchy/hooks/post-install.sample deleted file mode 100644 index 57043cb..0000000 --- a/core/home/config/nomarchy/hooks/post-install.sample +++ /dev/null @@ -1,9 +0,0 @@ -#!/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." diff --git a/core/home/config/nomarchy/hooks/post-update.sample b/core/home/config/nomarchy/hooks/post-update.sample deleted file mode 100644 index ff2d2ac..0000000 --- a/core/home/config/nomarchy/hooks/post-update.sample +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# This hook is called after an Nomarchy system update has been performed. -# To put it into use, remove .sample from the name. - -# Example: Show notification after the system has been updated. -# notify-send -u low "Update Performed" "Your system is now up to date" diff --git a/core/home/config/nomarchy/hooks/theme-set.sample b/core/home/config/nomarchy/hooks/theme-set.sample deleted file mode 100644 index 2d13287..0000000 --- a/core/home/config/nomarchy/hooks/theme-set.sample +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# This hook is called with the snake-cased name of the theme that has just been set. -# To put it into use, remove .sample from the name. - -# Example: Show the name of the theme that was just set. -# notify-send -u low "New theme" "Your new theme is $1" diff --git a/core/home/config/nomarchy/themed/alacritty.toml.tpl.sample b/core/home/config/nomarchy/themed/alacritty.toml.tpl.sample deleted file mode 100644 index cd0ec65..0000000 --- a/core/home/config/nomarchy/themed/alacritty.toml.tpl.sample +++ /dev/null @@ -1,86 +0,0 @@ -# Nomarchy User Template Example: Alacritty -# -# This is a sample template file demonstrating how to create custom themed -# configurations. Templates use placeholder variables that get replaced with -# your current theme's colors when you switch themes. -# -# HOW TO USE: -# 1. Rename this file to: alacritty.toml.tpl (remove the .sample extension) -# 2. Customize it as needed -# 3. Change your theme to apply the template -# -# User templates in ~/.config/nomarchy/themed/ take priority over the built-in -# templates in the Nomarchy default/themed/ directory. This lets you override -# any built-in template or create templates for applications not included -# by default. -# -# AVAILABLE VARIABLES: -# {{ background }} - Main background color (e.g., "#1a1b26") -# {{ foreground }} - Main foreground/text color -# {{ cursor }} - Cursor color -# {{ accent }} - Theme accent color -# {{ selection_background }} - Selection highlight background -# {{ selection_foreground }} - Selection highlight foreground -# -# {{ color0 }} through {{ color15 }} - Standard 16-color terminal palette -# color0-7: Normal colors (black, red, green, yellow, blue, magenta, cyan, white) -# color8-15: Bright variants -# -# VARIABLE MODIFIERS: -# {{ variable_strip }} - Hex color without the # prefix (e.g., "1a1b26") -# {{ variable_rgb }} - Decimal RGB values (e.g., "26,27,38") -# -# Example using modifiers: -# background = "{{ background }}" -> background = "#1a1b26" -# background = "{{ background_strip }}" -> background = "1a1b26" -# background = "rgb({{ background_rgb }})" -> background = "rgb(26,27,38)" -# -# --------------------------------------------------------------------------- - -[colors.primary] -background = "{{ background }}" -foreground = "{{ foreground }}" - -[colors.cursor] -text = "{{ background }}" -cursor = "{{ cursor }}" - -[colors.vi_mode_cursor] -text = "{{ background }}" -cursor = "{{ cursor }}" - -[colors.search.matches] -foreground = "{{ background }}" -background = "{{ color3 }}" - -[colors.search.focused_match] -foreground = "{{ background }}" -background = "{{ color1 }}" - -[colors.footer_bar] -foreground = "{{ background }}" -background = "{{ foreground }}" - -[colors.selection] -text = "{{ selection_foreground }}" -background = "{{ selection_background }}" - -[colors.normal] -black = "{{ color0 }}" -red = "{{ color1 }}" -green = "{{ color2 }}" -yellow = "{{ color3 }}" -blue = "{{ color4 }}" -magenta = "{{ color5 }}" -cyan = "{{ color6 }}" -white = "{{ color7 }}" - -[colors.bright] -black = "{{ color8 }}" -red = "{{ color9 }}" -green = "{{ color10 }}" -yellow = "{{ color11 }}" -blue = "{{ color12 }}" -magenta = "{{ color13 }}" -cyan = "{{ color14 }}" -white = "{{ color15 }}" diff --git a/core/home/config/starship.toml b/core/home/config/starship.toml deleted file mode 100644 index 36f15d1..0000000 --- a/core/home/config/starship.toml +++ /dev/null @@ -1,32 +0,0 @@ -add_newline = true -command_timeout = 200 -format = "[$directory$git_branch$git_status]($style)$character" - -[character] -error_symbol = "[✗](bold cyan)" -success_symbol = "[❯](bold cyan)" - -[directory] -truncation_length = 2 -truncation_symbol = "…/" -repo_root_style = "bold cyan" -repo_root_format = "[$repo_root]($repo_root_style)[$path]($style)[$read_only]($read_only_style) " - -[git_branch] -format = "[$branch]($style) " -style = "italic cyan" - -[git_status] -format = '[$all_status]($style)' -style = "cyan" -ahead = "⇡${count} " -diverged = "⇕⇡${ahead_count}⇣${behind_count} " -behind = "⇣${count} " -conflicted = " " -up_to_date = " " -untracked = "? " -modified = " " -stashed = "" -staged = "" -renamed = "" -deleted = "" diff --git a/core/home/config/uwsm/default b/core/home/config/uwsm/default deleted file mode 100644 index 946fbe5..0000000 --- a/core/home/config/uwsm/default +++ /dev/null @@ -1,13 +0,0 @@ -# Changes require a restart to take effect. - -# Install other terminals via Install > Terminal -export TERMINAL=xdg-terminal-exec - -# Use code for VSCode -export EDITOR=nvim - -# Use a custom directory for screenshots (remember to make the directory!) -# export OMARCHY_SCREENSHOT_DIR="$HOME/Pictures/Screenshots" - -# Use a custom directory for screenrecordings (remember to make the directory!) -# export OMARCHY_SCREENRECORD_DIR="$HOME/Videos/Screencasts" diff --git a/core/home/config/uwsm/env b/core/home/config/uwsm/env deleted file mode 100644 index ff7f463..0000000 --- a/core/home/config/uwsm/env +++ /dev/null @@ -1,11 +0,0 @@ -# Changes require a restart to take effect. - -# Ensure Nomarchy bins are in the path -export OMARCHY_PATH=$HOME/.local/share/nomarchy -export PATH=$OMARCHY_PATH/bin:$PATH:$HOME/.local/bin - -# Set default terminal and editor -source ~/.config/uwsm/default - -# Activate mise if present on the system -nomarchy-cmd-present mise && eval "$(mise activate bash --shims)" diff --git a/core/home/config/wiremix/wiremix.toml b/core/home/config/wiremix/wiremix.toml deleted file mode 100644 index 07f9747..0000000 --- a/core/home/config/wiremix/wiremix.toml +++ /dev/null @@ -1,5 +0,0 @@ -# overwrites default wiremix configuration -# defaults: https://github.com/tsowell/wiremix/blob/main/wiremix.toml - -[char_sets.default] -default_device = "⮞" diff --git a/core/home/config/xdg-terminals.list b/core/home/config/xdg-terminals.list deleted file mode 100644 index 0821cf0..0000000 --- a/core/home/config/xdg-terminals.list +++ /dev/null @@ -1,3 +0,0 @@ -# Terminal emulator preference order for xdg-terminal-exec -# The first found and valid terminal will be used -Alacritty.desktop diff --git a/core/home/configs.nix b/core/home/configs.nix deleted file mode 100644 index ef82d22..0000000 --- a/core/home/configs.nix +++ /dev/null @@ -1,106 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - configDir = ./config; - - # Explicit list of config items to manage - # This replaces dynamic builtins.readDir for clarity and faster evaluation - configItems = { - # Directories - fastfetch = "directory"; - fcitx5 = "directory"; - fontconfig = "directory"; - git = "directory"; - imv = "directory"; - "nautilus-python" = "directory"; - nomarchy = "directory"; - "nomarchy-skill" = "directory"; - uwsm = "directory"; - wiremix = "directory"; - - # Files - "brave-flags.conf" = "regular"; - "chromium-flags.conf" = "regular"; - "starship.toml" = "regular"; - "xdg-terminals.list" = "regular"; - }; - - # Files/directories handled elsewhere or not intended for ~/.config/ - # - nomarchy.ttf: font file, not a config - # - waybar: handled in waybar.nix - # - walker: handled in walker.nix - # - alacritty: handled in alacritty.nix - # - swayosd: handled in swayosd.nix - - # Check for user overrides - userConfigDir = config.nomarchy.configOverrides; - - # Generate the xdg.configFile attribute set - makeMapping = name: type: - let - source = if userConfigDir != null then "${userConfigDir}/${name}" else "${configDir}/${name}"; - in { - inherit name; - value = lib.mkDefault { - inherit source; - recursive = type == "directory"; - }; - }; - - configMappings = lib.mapAttrs' (name: type: makeMapping name type) configItems; - -in -{ - xdg.configFile = configMappings // { - # mako reads ~/.config/mako/config by default. The themed Nomarchy - # config (urgency rules, app filters, button handlers) lives under - # nomarchy/default/mako/core.ini for organizational reasons, so wire - # it explicitly here. Without this, mako silently falls back to its - # built-in defaults and every Nomarchy notification customization is - # inert. - "mako/config".source = lib.mkDefault ./config/nomarchy/default/mako/core.ini; - - # Hyprland's native-Wayland input config — templated from - # nomarchy.keymap.{layout,variant} so non-US users get the right - # layout in Wayland apps. The previous static input.conf hardcoded - # `kb_layout = us`, so the installer's xkb.layout / console.keyMap - # writes only reached XWayland and the TTY. Generated here rather - # than shipped under config/nomarchy/default/hypr/ so the keymap - # values can vary per-host without a source-file edit. - "nomarchy/default/hypr/input.conf".text = lib.mkDefault '' - # https://wiki.hyprland.org/Configuring/Variables/#input - input { - kb_layout = ${config.nomarchy.keymap.layout} - kb_variant = ${config.nomarchy.keymap.variant} - kb_model = - kb_options = compose:caps - kb_rules = - - follow_mouse = 1 - - sensitivity = 0 # -1.0 - 1.0, 0 means no modification. - - touchpad { - natural_scroll = false - } - } - - misc { - key_press_enables_dpms = true # key press will trigger wake - mouse_move_enables_dpms = true # mouse move will trigger wake - } - ''; - }; - - home.file.".XCompose" = lib.mkDefault { - source = ./config/nomarchy/default/xcompose; - }; - - # Pristine copy of the stock configs, kept out of ~/.config so a user edit - # never touches it. `nomarchy-refresh-config` restores a file in ~/.config - # from here, and SKILL.md points users at it to diff against defaults. - xdg.dataFile."nomarchy/config".source = builtins.path { - name = "nomarchy-config"; - path = ./config; - }; -} diff --git a/core/home/default.nix b/core/home/default.nix deleted file mode 100644 index b5fe8fe..0000000 --- a/core/home/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ ... }: - -{ - imports = [ - ./options.nix - ./state.nix - ./overrides.nix - ./fonts.nix - ./configs.nix - ./security.nix - ./bash.nix - ./gaming.nix - ./accessibility.nix - ]; -} diff --git a/core/home/fonts.nix b/core/home/fonts.nix deleted file mode 100644 index 96c959c..0000000 --- a/core/home/fonts.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ lib, ... }: - -{ - config = { - fonts.fontconfig.enable = lib.mkDefault true; - }; -} diff --git a/core/home/gaming.nix b/core/home/gaming.nix deleted file mode 100644 index c073fcd..0000000 --- a/core/home/gaming.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ config, lib, ... }: - -let - cfg = config.nomarchy.gaming; -in -{ - config = lib.mkIf cfg.enable { - wayland.windowManager.hyprland.extraConfig = '' - windowrulev2 = fullscreen, class:^(steam_app_).*$ - ''; - }; -} diff --git a/core/home/options.nix b/core/home/options.nix deleted file mode 100644 index 206673a..0000000 --- a/core/home/options.nix +++ /dev/null @@ -1,218 +0,0 @@ -{ lib, pkgs, ... }: - -let - # Defaults live in lib/state-schema.nix so they can't drift between this - # file, core/system/options.nix, and core/home/state.nix's `or` fallbacks. - schema = import ../../lib/state-schema.nix { inherit lib; }; -in -{ - options.nomarchy = { - toggles = { - suspend = lib.mkOption { - type = lib.types.bool; - default = schema.home.suspend; - description = "Whether to show suspend in system menu."; - }; - screensaver = lib.mkOption { - type = lib.types.bool; - default = schema.home.screensaver; - description = "Whether the screensaver is enabled."; - }; - idle = lib.mkOption { - type = lib.types.bool; - default = schema.home.idle; - description = "Whether the idle lock is enabled."; - }; - nightlight = lib.mkOption { - type = lib.types.bool; - default = schema.home.nightlight; - description = "Whether the nightlight is enabled."; - }; - waybar = lib.mkOption { - type = lib.types.bool; - default = schema.home.waybar; - description = "Whether the top bar is enabled."; - }; - }; - nightlightTemperature = lib.mkOption { - type = lib.types.int; - default = schema.home.nightlightTemperature; - description = "Temperature for the nightlight."; - }; - theme = lib.mkOption { - type = lib.types.str; - default = schema.home.theme; - 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" ]; - default = "laptop"; - description = '' - Physical form factor. Drives UI affordances (battery widget, - future lid handling). Default "laptop" — battery widget is - harmless on a desktop (renders empty when no BAT* is present), - so the safe default is "show, don't hide". The installer - auto-detects via /sys/class/power_supply/BAT* and writes the - explicit value into the generated home.nix. - ''; - }; - keymap = { - layout = lib.mkOption { - type = lib.types.str; - default = "us"; - example = "dk"; - description = '' - Keyboard layout for Hyprland's native Wayland session. The - installer also writes services.xserver.xkb.layout (for XWayland) - and console.keyMap (for the TTY) to the same value via - system.nix, but Hyprland reads its own input config so it needs - this option independently — otherwise non-US users get the - right layout in XWayland apps and the console but the US - fallback inside native-Wayland apps. - ''; - }; - variant = lib.mkOption { - type = lib.types.str; - default = ""; - example = "intl"; - description = '' - Keyboard variant for Hyprland (e.g. "intl" for US-International). - Empty by default. - ''; - }; - }; - wallpaper = lib.mkOption { - type = lib.types.str; - default = schema.home.wallpaper; - 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. 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. Driven by the declarative state."; - }; - gaps_out = lib.mkOption { - type = lib.types.int; - default = schema.home.hyprland.gaps_out; - 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. 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. 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 { - type = lib.types.str; - default = "Yaru-blue"; - description = "System icon theme name."; - }; - isLightMode = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether the current theme is light mode."; - }; - cursor = { - name = lib.mkOption { - type = lib.types.str; - default = "Bibata-Modern-Ice"; - description = "Mouse cursor theme name."; - }; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.bibata-cursors; - description = "Package providing the cursor theme."; - }; - }; - configOverrides = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to a directory containing configuration overrides."; - }; - - apps = { - alacritty.enable = lib.mkEnableOption "Alacritty terminal integration"; - btop.enable = lib.mkEnableOption "btop resource monitor integration"; - ghostty.enable = lib.mkEnableOption "Ghostty terminal integration"; - kitty.enable = lib.mkEnableOption "Kitty terminal integration"; - lazygit.enable = lib.mkEnableOption "lazygit integration"; - tmux.enable = lib.mkEnableOption "tmux integration"; - elephant.enable = lib.mkEnableOption "Elephant menu provider integration"; - walker.enable = lib.mkEnableOption "Walker launcher integration"; - swayosd.enable = lib.mkEnableOption "SwayOSD integration"; - vscode = { - enable = lib.mkEnableOption "VSCode integration"; - devExtensions = lib.mkEnableOption "Nomarchy's curated VSCode extension pack (language servers + git + editor enhancements)"; - }; - opencode.enable = lib.mkEnableOption '' - opencode AI coding CLI integration. When on, deploys - ~/.config/opencode/opencode.json. The `opencode` package itself - is not installed by Nomarchy — add it to your home.nix if you - want it on PATH. - ''; - }; - - gaming = { - enable = lib.mkEnableOption '' - Gaming preset (home-side companion to nomarchy.system.gaming.enable). - Adds a Hyprland window rule that fullscreens windows whose class - matches steam_app_* — i.e. games launched through Steam — so they - grab the whole screen instead of opening windowed. Set this to the - same value as nomarchy.system.gaming.enable; the installer flips - both when the Gaming profile is selected. - ''; - }; - - accessibility = { - enable = lib.mkEnableOption '' - Accessibility preset (home-side companion to - nomarchy.system.accessibility.enable). Adjusts the Hyprland - Wayland session in three ways: slows input.repeat_rate to 25 - (from 40) and input.repeat_delay to 1000 ms (from 600) so - holding a key isn't a runaway machine-gun for users with - low-mobility hands; binds SUPER+ALT+S to launch the Orca screen - reader (the system preset already puts orca on PATH); and is - the gate behind which a future high-contrast palette will hide. - Set this to the same value as nomarchy.system.accessibility.enable. - ''; - }; - }; -} diff --git a/core/home/overrides.nix b/core/home/overrides.nix deleted file mode 100644 index b4fdd3c..0000000 --- a/core/home/overrides.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ config, lib, ... }: - -# File-based override system for Nomarchy. -# -# When `nomarchy.overrides.enable = true` (default), every entry in -# `nomarchy.overrides.paths` substitutes the `xdg.configFile.<key>.source` -# Nomarchy ships by default. The substitution is done with `lib.mkForce` -# so it wins over Nomarchy's own `lib.mkDefault` writes. Other fields on -# the original entry (e.g. `recursive`) survive the merge. -# -# Usage in a downstream home.nix: -# -# nomarchy.overrides.paths = { -# "nomarchy/default/hypr/looknfeel.conf" = ./mine/looknfeel.conf; -# "waybar/style.css" = ./mine/waybar.css; -# }; -# -# The keys are xdg.configFile paths (i.e. relative to ~/.config/). Drop- -# in-a-dir discovery is intentionally not supported — Nix needs every -# managed file declared at evaluation time, and the attrset is the -# explicit-config shape the rest of the Nomarchy module surface uses. - -let - cfg = config.nomarchy.overrides; -in -{ - options.nomarchy.overrides = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Whether file-based overrides in nomarchy.overrides.paths are - applied. Defaults to true so an empty `paths` is a no-op and - a populated one Just Works without a second toggle. - ''; - }; - - paths = lib.mkOption { - type = lib.types.attrsOf lib.types.path; - default = {}; - example = lib.literalExpression '' - { - "nomarchy/default/hypr/looknfeel.conf" = ./looknfeel.conf; - "waybar/style.css" = ./waybar.css; - } - ''; - description = '' - Attribute set mapping xdg.configFile paths (relative to - ~/.config/) to Nix paths whose contents should replace the - Nomarchy-shipped source. Each entry deploys at lib.mkForce - priority so it overrides Nomarchy's lib.mkDefault writes; - recursive flags and other fields on the original entry are - preserved. Pointing at a path Nomarchy doesn't manage just - creates a new xdg.configFile entry. - ''; - }; - }; - - config = lib.mkIf cfg.enable { - xdg.configFile = lib.mapAttrs (_path: src: { - source = lib.mkForce src; - }) cfg.paths; - }; -} diff --git a/core/home/security.nix b/core/home/security.nix deleted file mode 100644 index 67fbdfd..0000000 --- a/core/home/security.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ pkgs, ... }: - -{ - systemd.user.services.polkit-gnome-authentication-agent-1 = { - Unit = { - Description = "Polkit GNOME Authentication Agent"; - After = [ "graphical-session.target" ]; - PartOf = [ "graphical-session.target" ]; - }; - - Service = { - ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; - Restart = "on-failure"; - RestartSec = 1; - TimeoutStopSec = 10; - }; - - Install = { - WantedBy = [ "graphical-session.target" ]; - }; - }; -} diff --git a/core/home/state.nix b/core/home/state.nix deleted file mode 100644 index 008ea05..0000000 --- a/core/home/state.nix +++ /dev/null @@ -1,65 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - nomarchyLib = import ../../lib { inherit lib; }; - # Single source of truth for default values when state.json is missing - # a key. Both core/system/options.nix and core/home/options.nix read - # from this same file — changing a default in one place updates - # everywhere. (Was: each consumer hardcoded its own `or X` literal, - # which is how the summer-night/nord split lived for so long.) - schema = import ../../lib/state-schema.nix { inherit lib; }; - assetsPath = ../../themes/palettes; - - # Read unified state from ~/.config/nomarchy/state.json - togglesState = nomarchyLib.readHomeState config.home.homeDirectory; - - cfg = config.nomarchy; - # The resolved values, in state.json's on-disk shape. Seeded into the file - # so runtime consumers (nomarchy-installed-summary, the setters) see the - # real defaults instead of a missing key — without this the summary shows - # "—" for theme/font/panel on any system the installer didn't write to. - seedJSON = builtins.toJSON { - inherit (cfg) theme wallpaper panelPosition nightlightTemperature; - font = cfg.fonts.monospace; - inherit (cfg.toggles) suspend screensaver idle nightlight waybar; - inherit (cfg.hyprland) gaps_in gaps_out border_size; - }; - stateFile = "${config.home.homeDirectory}/.config/nomarchy/state.json"; -in -{ - # Every assignment uses lib.mkDefault so a downstream /etc/nixos/home.nix - # 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 = { - # Derived properties from the theme directory - isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode { - themeName = cfg.theme; - inherit assetsPath; - }); - iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme { - themeName = cfg.theme; - inherit assetsPath; - }); - }; - - # Backfill any state.json key the user hasn't set. Existing values always - # win (`defaults * existing`), so a user's theme/font/etc. and any - # runtime-only keys (welcome_done, …) are never clobbered — this only - # fills the gaps so the file reflects the system's actual defaults. - home.activation.seedNomarchyState = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - run mkdir -p "$(dirname ${lib.escapeShellArg stateFile})" - tmp="$(${pkgs.coreutils}/bin/mktemp)" - # Build the next file contents in $tmp first; only the final move is - # guarded by `run` so a dry-run never mutates the real state file. - if [ -e ${lib.escapeShellArg stateFile} ]; then - ${pkgs.jq}/bin/jq -n --argjson d ${lib.escapeShellArg seedJSON} \ - --slurpfile s ${lib.escapeShellArg stateFile} '$d * $s[0]' > "$tmp" - else - printf '%s\n' ${lib.escapeShellArg seedJSON} > "$tmp" - fi - run mv "$tmp" ${lib.escapeShellArg stateFile} - ''; - }; -} diff --git a/core/system/accessibility.nix b/core/system/accessibility.nix deleted file mode 100644 index 4e9d5ed..0000000 --- a/core/system/accessibility.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.nomarchy.system.accessibility; -in -{ - config = lib.mkIf cfg.enable { - services.gnome.at-spi2-core.enable = lib.mkDefault true; - - environment.systemPackages = [ pkgs.orca ]; - - environment.variables.XCURSOR_SIZE = toString cfg.cursorSize; - }; -} diff --git a/core/system/audio.nix b/core/system/audio.nix deleted file mode 100644 index 33f6958..0000000 --- a/core/system/audio.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - security.rtkit.enable = lib.mkDefault true; - services.pipewire = { - enable = lib.mkDefault true; - alsa.enable = lib.mkDefault true; - alsa.support32Bit = lib.mkDefault true; - pulse.enable = lib.mkDefault true; - jack.enable = lib.mkDefault true; - }; -} diff --git a/core/system/bluetooth.nix b/core/system/bluetooth.nix deleted file mode 100644 index dc57c0d..0000000 --- a/core/system/bluetooth.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - hardware.bluetooth.enable = lib.mkDefault true; - hardware.bluetooth.powerOnBoot = lib.mkDefault true; - services.blueman.enable = lib.mkDefault true; -} diff --git a/core/system/branding.nix b/core/system/branding.nix deleted file mode 100644 index 77f23a9..0000000 --- a/core/system/branding.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ lib, ... }: - -{ - # Identify the distribution as Nomarchy in /etc/os-release. - # Anything that reads os-release picks this up: fastfetch, login banners, - # GUI "About" dialogs, neofetch, etc. - system.nixos = { - distroId = "nomarchy"; - distroName = "Nomarchy"; - }; - - # Ensure the bootloader entries use Nomarchy instead of NixOS - boot.loader.grub.configurationName = lib.mkDefault "Nomarchy"; - # For systemd-boot, NixOS 24.11+ uses distroName if available, - # but some older versions or specific setups might need explicit labels. - # We use mkDefault so users can still override if they want. -} diff --git a/core/system/browser.nix b/core/system/browser.nix deleted file mode 100644 index 5efbe79..0000000 --- a/core/system/browser.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - nomarchyLib = import ../../lib { inherit lib; }; - activeThemeName = config.nomarchy.system.theme; - currentPalette = nomarchyLib.getPalette activeThemeName; - - # Hex color for browser theme (base00 is background) - themeColor = "#${currentPalette.base00}"; - - # Detect light mode from theme name or palette - isLightTheme = nomarchyLib.isThemeLightMode { - themeName = activeThemeName; - assetsPath = ../../themes/palettes; - }; - - browserPolicy = { - BrowserThemeColor = themeColor; - BrowserColorScheme = if isLightTheme then "light" else "dark"; - }; -in -{ - # Chromium policies - programs.chromium.extraOpts = lib.mkDefault browserPolicy; - - # Brave browser policies via managed policy file - environment.etc."brave/policies/managed/nomarchy.json".text = lib.mkDefault ( - builtins.toJSON browserPolicy - ); -} diff --git a/core/system/containers.nix b/core/system/containers.nix deleted file mode 100644 index 491579e..0000000 --- a/core/system/containers.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.nomarchy.system.containers; -in -{ - config = lib.mkIf cfg.enable { - virtualisation.podman = { - enable = true; - # `docker` and `docker-compose` invocations transparently route to - # podman. Pairs cleanly with rootless mode. - dockerCompat = true; - defaultNetwork.settings.dns_enabled = true; - }; - - environment.systemPackages = with pkgs; [ - podman - podman-compose - podman-tui - dive - ]; - }; -} diff --git a/core/system/default.nix b/core/system/default.nix deleted file mode 100644 index 9d2530e..0000000 --- a/core/system/default.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ config, lib, pkgs, ... }: - -{ - imports = [ - ./options.nix - ./state.nix - ./branding.nix - ./graphics.nix - ./nix.nix - ./scripts.nix - ./systemd.nix - ./session.nix - ./virtualization.nix - ./fonts.nix - ./hardware.nix - ./audio.nix - ./bluetooth.nix - ./network.nix - ./impermanence.nix - ./browser.nix - ./file-manager.nix - # Tier 1 system features (all opt-in via nomarchy.system.*). - ./snapper.nix - ./laptop.nix - ./desktop.nix - ./accessibility.nix - ./gaming.nix - ./hibernate.nix - ./containers.nix - ./pam.nix - ./input-method.nix - ../../themes/engine/plymouth.nix - ../../themes/engine/sddm.nix - ]; - - time.timeZone = lib.mkDefault config.nomarchy.system.timezone; - - boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest; - - nixpkgs.config.allowUnfree = true; -} diff --git a/core/system/desktop.nix b/core/system/desktop.nix deleted file mode 100644 index e627452..0000000 --- a/core/system/desktop.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ config, lib, ... }: - -let - cfg = config.nomarchy.system.desktop; -in -{ - config = lib.mkIf cfg.enable { - powerManagement.cpuFreqGovernor = lib.mkDefault "performance"; - - # No-op until the user adds ZFS to boot.supportedFilesystems and a pool; - # then maintenance kicks in without further config. - services.zfs.autoScrub.enable = lib.mkDefault true; - services.zfs.trim.enable = lib.mkDefault true; - }; -} diff --git a/core/system/file-manager.nix b/core/system/file-manager.nix deleted file mode 100644 index 0aa206d..0000000 --- a/core/system/file-manager.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ 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 - ]; -} diff --git a/core/system/fonts.nix b/core/system/fonts.nix deleted file mode 100644 index 3a06321..0000000 --- a/core/system/fonts.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ pkgs, ... }: - -let - nomarchy-font = pkgs.stdenv.mkDerivation { - pname = "nomarchy-font"; - version = "1.0"; - # Point directly to the font file - src = ./../branding/Nomarchy.ttf; - # No archive to unpack - unpackPhase = "true"; - installPhase = '' - mkdir -p $out/share/fonts/truetype - cp $src $out/share/fonts/truetype/Nomarchy.ttf - ''; - }; -in -{ - fonts.packages = [ - nomarchy-font - pkgs.nerd-fonts.jetbrains-mono - pkgs.nerd-fonts.roboto-mono - pkgs.nerd-fonts.fira-code - pkgs.nerd-fonts.ubuntu-mono - ]; -} diff --git a/core/system/gaming.nix b/core/system/gaming.nix deleted file mode 100644 index 4290d00..0000000 --- a/core/system/gaming.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.nomarchy.system.gaming; -in -{ - config = lib.mkIf cfg.enable { - programs.steam = { - enable = true; - remotePlay.openFirewall = lib.mkDefault true; - localNetworkGameTransfers.openFirewall = lib.mkDefault true; - }; - - # gamemode adjusts CPU governor and reschedules processes when a - # game requests it. The launching user must be in the `gamemode` group. - programs.gamemode.enable = true; - - services.flatpak.enable = true; - - # `services.flatpak.enable = true` ships flatpak but does NOT add any - # remotes — without a remote, `flatpak install` and the Discover GUI - # can't find anything. nixpkgs has no declarative remote-add API yet, - # so we run a one-shot system unit after flatpak.service is ready that - # adds the flathub remote idempotently (`--if-not-exists`). Lives under - # the gaming preset because Pillar 5 ships flatpak as part of the - # gaming wiring; if another preset later needs flatpak, lift this to a - # dedicated module. - systemd.services.nomarchy-flathub-init = { - description = "Register the Flathub remote on first start"; - wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; - wants = [ "network-online.target" ]; - path = [ pkgs.flatpak ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - script = '' - flatpak remote-add --if-not-exists flathub \ - https://dl.flathub.org/repo/flathub.flatpakrepo - ''; - }; - }; -} diff --git a/core/system/graphics.nix b/core/system/graphics.nix deleted file mode 100644 index 0fd9da4..0000000 --- a/core/system/graphics.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ lib, ... }: - -{ - # Enable OpenGL/Graphics for Wayland Compositors (Hyprland) - # This provides better resolution and smoothness for all platforms. - hardware.graphics = { - enable = lib.mkDefault true; - enable32Bit = lib.mkDefault true; - }; -} diff --git a/core/system/hardware.nix b/core/system/hardware.nix deleted file mode 100644 index fada673..0000000 --- a/core/system/hardware.nix +++ /dev/null @@ -1,116 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; -let - cfg = config.nomarchy.hardware; -in -{ - options.nomarchy.hardware = { - isXPS = mkEnableOption "Dell XPS specific hardware fixes"; - isT2Mac = mkEnableOption "Apple T2 MacBook specific hardware fixes"; - isFramework = mkEnableOption "Framework laptop specific hardware fixes"; - hasIPU7Camera = mkEnableOption "Intel IPU7 camera support"; - fwupd = mkOption { - type = types.bool; - default = false; - description = "Whether to enable fwupd firmware update service."; - }; - }; - - config = mkMerge [ - (mkIf cfg.fwupd { - services.fwupd.enable = true; - }) - - (mkIf cfg.isXPS { - services.udev.extraRules = '' - ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:00:19.0", ATTR{power/control}="on" - ACTION=="add", SUBSYSTEM=="platform", KERNEL=="i2c_designware.0", ATTR{power/control}="on" - ''; - - systemd.services.nomarchy-haptic-touchpad = { - description = "Dell XPS haptic touchpad feedback"; - after = [ "systemd-udev-settle.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "simple"; - ExecStart = "${pkgs.nomarchy-system-scripts}/bin/nomarchy-haptic-touchpad"; - Restart = "on-failure"; - RestartSec = "2"; - }; - }; - }) - - (mkIf cfg.isFramework { - services.udev.extraRules = '' - # Framework 16 QMK HID - SUBSYSTEM=="usb", ATTR{idVendor}=="32ac", ATTR{idProduct}=="0012", MODE="0666", GROUP="users" - ''; - }) - - (mkIf cfg.isT2Mac { - boot.kernelParams = [ "intel_iommu=on" "iommu=pt" "pcie_ports=compat" ]; - boot.kernelModules = [ "apple-bce" ]; - boot.extraModprobeConfig = '' - options brcmfmac feature_disable=0x82000 - ''; - }) - - # System Features - (mkIf config.nomarchy.system.features.fingerprint { - services.fprintd.enable = true; - }) - - (mkIf config.nomarchy.system.features.fido2 { - security.pam.u2f = { - enable = true; - control = "sufficient"; - cue = true; - }; - }) - - (mkIf config.nomarchy.system.features.hybridGPU { - # supergfxd manages mode switching (Integrated / Hybrid / Vfio / - # AsusEgpu). It blacklists/unblacklists the nvidia kernel module via - # /etc/modprobe.d/ depending on the active mode. ExecStartPre sleep - # gives udev time to settle so the daemon doesn't see a half-attached - # GPU on cold boot. - services.supergfxd.enable = true; - systemd.services.supergfxd.serviceConfig.ExecStartPre = "-${pkgs.coreutils}/bin/sleep 5"; - - # Load the NVIDIA driver so the dGPU has something to drive. Without - # these, supergfxd switches modes successfully but the X/Wayland - # stack has no NVIDIA driver loaded — render-offload silently no-ops - # and Hyprland renders everything on the iGPU regardless of mode. - # mkDefault throughout so downstream system.nix can override - # (pin to a beta driver, flip to the open kernel module, etc.). - services.xserver.videoDrivers = lib.mkDefault [ "nvidia" ]; - hardware.graphics.enable = lib.mkDefault true; - hardware.graphics.enable32Bit = lib.mkDefault true; - hardware.nvidia = { - modesetting.enable = lib.mkDefault true; - powerManagement.enable = lib.mkDefault true; - open = lib.mkDefault false; - package = lib.mkDefault config.boot.kernelPackages.nvidiaPackages.stable; - }; - # Required for Wayland compositors (Hyprland) to render via NVIDIA. - boot.kernelParams = [ "nvidia-drm.modeset=1" ]; - - # PRIME render-offload (the part that lets `nvidia-offload <cmd>` - # actually use the dGPU) needs bus IDs, which are per-machine. - # We deliberately don't enable `hardware.nvidia.prime.offload.enable` - # here — without the correct intelBusId / nvidiaBusId the nvidia - # kernel module panics on load. The user adds this to their own - # /etc/nixos/system.nix after running `lspci -D`: - # - # hardware.nvidia.prime = { - # offload.enable = true; - # offload.enableOffloadCmd = true; - # intelBusId = "PCI:0:2:0"; # or amdgpuBusId for AMD iGPU - # nvidiaBusId = "PCI:1:0:0"; - # }; - # - # See docs/OPTIONS.md for the full recipe. - }) - ]; -} diff --git a/core/system/hibernate.nix b/core/system/hibernate.nix deleted file mode 100644 index fa09859..0000000 --- a/core/system/hibernate.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ config, lib, ... }: - -let - cfg = config.nomarchy.system.hibernation; -in -{ - config = lib.mkIf cfg.enable { - # Wait this long after suspend before hibernating, and use the same - # delay as the idle-action timeout so the two paths agree. - systemd.sleep.extraConfig = '' - HibernateDelaySec=${toString (cfg.idleMinutes * 60)} - ''; - - services.logind = { - settings.Login = { - HandleLidSwitch = lib.mkDefault "suspend-then-hibernate"; - HandleLidSwitchExternalPower = lib.mkDefault "suspend"; - HandlePowerKey = lib.mkDefault "hibernate"; - IdleAction = lib.mkDefault "suspend-then-hibernate"; - IdleActionSec = lib.mkDefault (toString (cfg.idleMinutes * 60)); - }; - }; - }; -} diff --git a/core/system/impermanence.nix b/core/system/impermanence.nix deleted file mode 100644 index d3990e4..0000000 --- a/core/system/impermanence.nix +++ /dev/null @@ -1,133 +0,0 @@ -{ config, lib, pkgs, inputs, ... }: - -let - cfg = config.nomarchy.system.impermanence; -in -{ - imports = [ - inputs.impermanence.nixosModules.impermanence - ]; - - options.nomarchy.system.impermanence = { - enable = lib.mkEnableOption "Erase Your Darlings (Impermanence) root wipe on boot"; - - # The disko layout names the main LUKS mapping `crypted` on single-disk - # installs and `crypted_main` on multi-disk installs (see - # installer/disko-config.nix: `mainLuksName`). The rollback hook must - # mount the right device, otherwise initrd fails on every boot and the - # @ → root-blank snapshot is never restored. - mainLuksName = lib.mkOption { - type = lib.types.str; - default = "crypted"; - description = '' - Name of the /dev/mapper entry holding the BTRFS root. Set to - "crypted_main" on multi-disk installs to match the disko layout. - ''; - }; - - user = lib.mkOption { - type = lib.types.str; - default = "nomarchy"; - description = '' - Primary user whose home subset (.ssh, .gnupg, keyrings, common - directories) survives the rootfs wipe. Must match the user - created via `users.users.<name>` — otherwise the persistence - block is silently inert and the user's home directory is wiped - on every boot. The installer writes this for you. - ''; - }; - }; - - config = lib.mkIf cfg.enable { - # 1. The Rollback Service: wipes the @ root subvolume back to a blank - # snapshot before the real root is mounted. Plymouth enables systemd - # stage-1 initrd distro-wide (themes/engine/plymouth.nix), and systemd - # stage 1 rejects boot.initrd.postDeviceCommands — so this runs as a - # systemd initrd unit ordered after the LUKS mapping opens and before - # sysroot is mounted. initrdBin pulls in the binaries the script calls - # (the systemd initrd doesn't ship coreutils/findutils/btrfs by default). - boot.initrd.systemd.initrdBin = [ - pkgs.btrfs-progs - pkgs.coreutils - pkgs.util-linux - pkgs.findutils - ]; - - boot.initrd.systemd.services.nomarchy-rollback = { - description = "Erase Your Darlings: roll the BTRFS root subvolume back to a blank snapshot"; - wantedBy = [ "initrd.target" ]; - after = [ "systemd-cryptsetup@${cfg.mainLuksName}.service" ]; - before = [ "sysroot.mount" ]; - unitConfig.DefaultDependencies = "no"; - serviceConfig.Type = "oneshot"; - script = '' - mkdir -p /btrfs_tmp - mount -o subvol=/ /dev/mapper/${cfg.mainLuksName} /btrfs_tmp - - if [[ -e /btrfs_tmp/@ ]]; then - mkdir -p /btrfs_tmp/old_roots - timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/@)" "+%Y-%m-%-d_%H:%M:%S") - mv /btrfs_tmp/@ "/btrfs_tmp/old_roots/$timestamp" - fi - - delete_subvolume_recursively() { - IFS=$'\n' - for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do - delete_subvolume_recursively "/btrfs_tmp/$i" - done - btrfs subvolume delete "$1" - } - - for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +30); do - delete_subvolume_recursively "$i" - done - - btrfs subvolume snapshot /btrfs_tmp/root-blank /btrfs_tmp/@ - umount /btrfs_tmp - ''; - }; - - # 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; - directories = [ - "/var/log" - "/var/lib/nixos" - "/var/lib/systemd/coredump" - "/var/lib/NetworkManager" - "/etc/NetworkManager/system-connections" - "/var/lib/bluetooth" - "/var/lib/fprint" - "/etc/nixos" - "/etc/ssh" - ]; - files = [ - "/etc/machine-id" - "/etc/supergfxd.conf" - ]; - users.${cfg.user} = { - directories = [ - ".ssh" - ".gnupg" - ".local/share/keyrings" - "Documents" - "Downloads" - "Pictures" - "Videos" - "Projects" - ]; - }; - }; - }; -} diff --git a/core/system/input-method.nix b/core/system/input-method.nix deleted file mode 100644 index f1c8c64..0000000 --- a/core/system/input-method.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.nomarchy.system.inputMethod; -in -{ - config = lib.mkIf cfg.enable { - # NixOS's i18n.inputMethod handles env vars (GTK_IM_MODULE, QT_IM_MODULE, - # XMODIFIERS, SDL_IM_MODULE) and the fcitx5-daemon autostart for us, so - # we don't ship a manual environment.d file or Hyprland exec-once. - i18n.inputMethod = { - enable = true; - type = "fcitx5"; - fcitx5 = { - waylandFrontend = true; - addons = with pkgs; [ - fcitx5-mozc # Japanese - kdePackages.fcitx5-chinese-addons - fcitx5-table-extra # Hanyu/Cangjie/etc. - fcitx5-gtk # GTK4/3 client - ]; - }; - }; - }; -} diff --git a/core/system/laptop.nix b/core/system/laptop.nix deleted file mode 100644 index 54b8e3a..0000000 --- a/core/system/laptop.nix +++ /dev/null @@ -1,42 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.nomarchy.system.laptop; - hib = config.nomarchy.system.hibernation; - lidAction = if hib.enable then "suspend-then-hibernate" else "suspend"; -in -{ - config = lib.mkIf cfg.enable { - services.tlp = { - enable = lib.mkDefault true; - settings = { - CPU_SCALING_GOVERNOR_ON_AC = lib.mkDefault "performance"; - CPU_SCALING_GOVERNOR_ON_BAT = lib.mkDefault "powersave"; - CPU_BOOST_ON_BAT = lib.mkDefault 0; - PLATFORM_PROFILE_ON_AC = lib.mkDefault "balanced"; - PLATFORM_PROFILE_ON_BAT = lib.mkDefault "low-power"; - # Charge thresholds only honored on supported hardware (ThinkPad, - # some ASUS); a harmless warning is logged elsewhere. - START_CHARGE_THRESH_BAT0 = lib.mkDefault 75; - STOP_CHARGE_THRESH_BAT0 = lib.mkDefault 80; - }; - }; - - # TLP and power-profiles-daemon both arbitrate CPU/EPP — NixOS asserts - # mutual exclusion. Opt out of the preset entirely to use PPD instead. - services.power-profiles-daemon.enable = lib.mkForce false; - - services.upower.enable = lib.mkDefault true; - services.thermald.enable = lib.mkDefault cfg.thermald; - - # Backlight write access for the `video` group, so the existing - # nomarchy-brightness-{display,keyboard} scripts run without root. - services.udev.packages = [ pkgs.brightnessctl ]; - - services.logind.settings.Login = { - HandleLidSwitch = lib.mkDefault lidAction; - HandleLidSwitchExternalPower = lib.mkDefault "suspend"; - HandleLidSwitchDocked = lib.mkDefault "ignore"; - }; - }; -} diff --git a/core/system/network.nix b/core/system/network.nix deleted file mode 100644 index 9d7cb77..0000000 --- a/core/system/network.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - cfg = config.nomarchy.system; -in -{ - networking.networkmanager.enable = lib.mkDefault true; - - networking.networkmanager.wifi.powersave = lib.mkDefault cfg.wifi.powersave; - - # DNS Configuration - networking.nameservers = lib.mkDefault ( - if cfg.dns == "Cloudflare" then [ "1.1.1.1" "1.0.0.1" ] - else if cfg.dns == "Google" then [ "8.8.8.8" "8.8.4.4" ] - else if cfg.dns == "Custom" then cfg.customDns - else [] # DHCP lets NM handle it - ); - - services.resolved = { - enable = lib.mkDefault (cfg.dns != "DHCP"); - dnssec = lib.mkDefault "allow-downgrade"; - domains = lib.mkDefault [ "~." ]; - fallbackDns = lib.mkDefault [ "9.9.9.9" "149.112.112.112" ]; - extraConfig = lib.mkDefault '' - DNSOverTLS=opportunistic - ''; - }; - - environment.systemPackages = [ - pkgs.networkmanagerapplet - ]; -} diff --git a/core/system/nix.nix b/core/system/nix.nix deleted file mode 100644 index 62fc380..0000000 --- a/core/system/nix.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ inputs, lib, ... }: - -{ - nix = { - settings = { - # Enable experimental features for modern Nix commands and flakes - experimental-features = [ "nix-command" "flakes" ]; - - # Allow users in the wheel group to manage the Nix store - trusted-users = [ "root" "@wheel" ]; - - # Optimize storage by hard-linking identical files - auto-optimise-store = lib.mkDefault true; - - # Bump the substituter download buffer from the 64 MiB default to - # 512 MiB. Large NARs (kernels, electron apps, the full NixOS - # closure on first install) overrun 64 MiB and Nix prints - # "download buffer is full" until the consumer catches up — the - # warning is harmless but slows substitution and looks like an - # error. 512 MiB comfortably covers everything in our closure. - download-buffer-size = 524288000; - }; - - # Populates NIX_PATH with the nixpkgs input from the flake. - # This is critical for making 'nix-shell -p ...' and other legacy - # Nix commands work in a flake-based environment without channels. - nixPath = [ "nixpkgs=${inputs.nixpkgs}" ]; - - # Map all flake inputs to the system registry. - # This ensures 'nix shell nixpkgs#...' uses the exact same version - # as the rest of the system and doesn't need to re-download. - registry = lib.mapAttrs (_: value: { flake = value; }) inputs; - }; -} diff --git a/core/system/options.nix b/core/system/options.nix deleted file mode 100644 index bc955f6..0000000 --- a/core/system/options.nix +++ /dev/null @@ -1,210 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - # Defaults live in lib/state-schema.nix so they can't drift between this - # file, core/home/options.nix, and core/home/state.nix's `or` fallbacks. - schema = import ../../lib/state-schema.nix { inherit lib; }; -in -{ - options.nomarchy.system = { - dns = lib.mkOption { - type = lib.types.enum [ "Cloudflare" "Google" "DHCP" "Custom" ]; - default = schema.system.dns; - description = "Selected DNS provider."; - }; - customDns = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = schema.system.customDns; - description = "List of custom DNS servers."; - }; - wifi = { - powersave = lib.mkOption { - type = lib.types.bool; - default = schema.system.wifi.powersave; - description = "Whether to enable wifi power saving."; - }; - }; - timezone = lib.mkOption { - type = lib.types.str; - default = schema.system.timezone; - description = "System timezone."; - }; - formFactor = lib.mkOption { - type = lib.types.enum [ "laptop" "desktop" ]; - default = "laptop"; - description = '' - Physical form factor. Drives UI affordances (battery widget, - future lid handling / TLP). Default "laptop" — battery widget - is harmless on a desktop (renders empty when no BAT* is - present), so the safe default is "show, don't hide". The - installer auto-detects via /sys/class/power_supply/BAT* and - writes the explicit value into the generated system.nix. - ''; - }; - features = { - fingerprint = lib.mkOption { - type = lib.types.bool; - default = schema.system.features.fingerprint; - description = "Whether to enable fingerprint support."; - }; - fido2 = lib.mkOption { - type = lib.types.bool; - default = schema.system.features.fido2; - description = "Whether to enable FIDO2 support."; - }; - hybridGPU = lib.mkOption { - type = lib.types.bool; - default = schema.system.features.hybridGPU; - description = "Whether to enable hybrid GPU support (supergfxd)."; - }; - }; - theme = lib.mkOption { - type = lib.types.str; - default = schema.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) --- - - snapper = { - enable = lib.mkEnableOption '' - Snapper-driven BTRFS timeline snapshots of `/`. Auto-disables when - `/` isn't BTRFS. Includes a `nixos-rebuild-snap` wrapper that takes - a "Pre-rebuild" snapshot before each switch. - ''; - }; - - hibernation = { - enable = lib.mkEnableOption '' - suspend-then-hibernate (lid close, idle, power button). NOTE: this - requires a disk swap device or swapfile sized to at least RAM — - zRAM alone is not enough. - ''; - idleMinutes = lib.mkOption { - type = lib.types.int; - default = 30; - description = "Idle minutes before suspend-then-hibernate fires."; - }; - }; - - laptop = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.nomarchy.system.formFactor == "laptop"; - defaultText = lib.literalExpression ''config.nomarchy.system.formFactor == "laptop"''; - description = '' - Laptop power preset: TLP (with sane AC/battery governors), - `services.upower`, `services.thermald` (x86_64), a brightnessctl - udev rule, and a logind lid-switch policy. Force-disables - `services.power-profiles-daemon` (mutually exclusive with TLP). - Lid-close defers to `nomarchy.system.hibernation.enable`: - suspend-then-hibernate when on, suspend otherwise. Defaults on - when `formFactor = "laptop"`. - ''; - }; - thermald = lib.mkOption { - type = lib.types.bool; - default = pkgs.stdenv.hostPlatform.isx86_64; - defaultText = lib.literalExpression "pkgs.stdenv.hostPlatform.isx86_64"; - description = '' - Enable `services.thermald` (Intel thermal daemon). Default true on - x86_64. Harmless no-op on AMD; gated off on aarch64. - ''; - }; - }; - - desktop = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.nomarchy.system.formFactor == "desktop"; - defaultText = lib.literalExpression ''config.nomarchy.system.formFactor == "desktop"''; - description = '' - Desktop preset: pins `powerManagement.cpuFreqGovernor` to - `"performance"` and enables `services.zfs.autoScrub` and - `services.zfs.trim` so a future ZFS pool gets sensible - maintenance without further config. The ZFS knobs are no-ops - until the user adds `boot.supportedFilesystems = [ "zfs" ]` - and a pool. Defaults on when `formFactor = "desktop"`. Battery - widget filtering is already handled by `formFactor` itself in - `features/desktop/waybar/default.nix`. - ''; - }; - }; - - accessibility = { - enable = lib.mkEnableOption '' - Accessibility preset: AT-SPI2 framework, the Orca screen reader - on PATH, and a larger default cursor size. Off by default — - accessibility is a personal preference, not a hardware-derived - signal. The Hyprland-side keybinding to launch Orca is a - separate roadmap item. - ''; - cursorSize = lib.mkOption { - type = lib.types.int; - default = 32; - description = '' - XCURSOR_SIZE when accessibility is on. NixOS default is 24; - 32 is a safer floor for low-vision users. Bump to 48 if the - user explicitly asks. - ''; - }; - }; - - gaming = { - enable = lib.mkEnableOption '' - Gaming preset: Steam (with remote-play firewall holes), - gamemode (CPU governor + nice on Steam launch via the user's - gamemode group), and flatpak. NOTE: flatpak's flathub remote - is not added declaratively — after first boot, run - `flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo`. - The Hyprland fullscreen-on-Steam-launch window rule is a - separate roadmap item. - ''; - }; - - containers = { - enable = lib.mkEnableOption '' - Rootless Podman with Docker compatibility (`docker` → `podman`), - plus podman-compose, podman-tui and dive. - ''; - }; - - virtualization = { - libvirt = { - enable = lib.mkEnableOption '' - libvirt daemon + virt-manager + OVMF. The user must be in the - `libvirtd` group. - ''; - }; - docker = { - enable = lib.mkEnableOption '' - Docker daemon + docker-compose. The user must be in the `docker` - group. - ''; - }; - }; - - keyring = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Auto-unlock GNOME Keyring at SDDM/Hyprland login and route SSH - keys through `gcr-ssh-agent`. Default on — near-universal QoL - improvement. - ''; - }; - }; - - inputMethod = { - enable = lib.mkEnableOption '' - fcitx5 input method (CJK / IME). Wires NixOS's i18n.inputMethod and - autostarts fcitx5-daemon. Adds a small footprint when enabled, so - most users want this off. - ''; - }; - }; -} diff --git a/core/system/pam.nix b/core/system/pam.nix deleted file mode 100644 index 14ced58..0000000 --- a/core/system/pam.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ config, lib, ... }: - -let - cfg = config.nomarchy.system.keyring; -in -{ - config = lib.mkIf cfg.enable { - # Auto-unlock GNOME Keyring at SDDM autologin and at login. hyprlock - # gets the same treatment so the session keyring stays unlocked when - # the screen lock disengages. - security.pam.services = { - login.enableGnomeKeyring = true; - sddm.enableGnomeKeyring = true; - hyprlock.enableGnomeKeyring = true; - }; - - # Run the keyring + the gcr SSH agent. Disabling `programs.ssh.startAgent` - # ensures keys flow through the keyring's agent (so unlock-on-login - # carries over to ssh) instead of a separate ssh-agent process. - services.gnome.gnome-keyring.enable = true; - services.gnome.gcr-ssh-agent.enable = true; - programs.ssh.startAgent = lib.mkForce false; - - # Point downstream tooling at the gcr socket so `ssh` / `git` / etc. - # find the keyring's keys without per-user shell config. - environment.sessionVariables.SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/gcr/ssh"; - }; -} diff --git a/core/system/scripts-derivation.nix b/core/system/scripts-derivation.nix deleted file mode 100644 index 76d3b03..0000000 --- a/core/system/scripts-derivation.nix +++ /dev/null @@ -1,63 +0,0 @@ -{ pkgs, lib, ... }: - -let - # System script dependencies - systemScriptDeps = with pkgs; [ - coreutils - gnused - gnugrep - findutils - gawk - jq - nixos-rebuild - pkgs.home-manager - git - sudo - brightnessctl - playerctl - pamixer - pciutils - usbutils - networkmanager - lshw - parted - btrfs-progs - cryptsetup - gum - curl - wget - libnotify - bc - supergfxctl - systemd - fwupd - hyprland - swayosd - python3 # nomarchy-haptic-touchpad is a python3 script (Dell XPS service) - ]; -in -pkgs.stdenv.mkDerivation { - pname = "nomarchy-system-scripts"; - version = "1.0.0"; - src = ./scripts; - - nativeBuildInputs = [ pkgs.makeWrapper ]; - - installPhase = '' - mkdir -p $out/bin - cp * $out/bin/ - - chmod +x $out/bin/* - patchShebangs $out/bin - ''; - - postFixup = '' - deps="${lib.makeBinPath systemScriptDeps}" - for file in $out/bin/*; do - if [ -f "$file" ]; then - wrapProgram "$file" \ - --prefix PATH : "$deps" - fi - done - ''; -} diff --git a/core/system/scripts.nix b/core/system/scripts.nix deleted file mode 100644 index 7ed306d..0000000 --- a/core/system/scripts.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ pkgs, ... }: - -{ - environment.systemPackages = [ pkgs.nomarchy-system-scripts ]; - - # /etc/nixos is owned by root, but `nomarchy-env-update` (and `nix - # flake` invocations) run as the user and shell out to git. Without - # this, git refuses with "dubious ownership in repository" and HM - # evaluation fails. Mark both the standard and impermanence-relocated - # paths as safe at the system level so every user is covered. - programs.git.config = { - safe.directory = [ "/etc/nixos" "/persist/etc/nixos" ]; - }; -} diff --git a/core/system/scripts/nomarchy-battery-capacity b/core/system/scripts/nomarchy-battery-capacity deleted file mode 100755 index eb73f3c..0000000 --- a/core/system/scripts/nomarchy-battery-capacity +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -# Returns the battery full capacity in Wh (rounded to whole number). -# Used by nomarchy-battery-status for displaying battery capacity. - -battery_info=$(upower -i $(upower -e | grep BAT)) - -echo "$battery_info" | awk '/energy-full:/ { - printf "%d", $2 - exit -}' diff --git a/core/system/scripts/nomarchy-battery-monitor b/core/system/scripts/nomarchy-battery-monitor deleted file mode 100755 index 8a45cf5..0000000 --- a/core/system/scripts/nomarchy-battery-monitor +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -e - -# Designed to be run by systemd timer every 30 seconds and alerts if battery is low - -BATTERY_THRESHOLD=10 -NOTIFICATION_FLAG="/run/user/$UID/nomarchy_battery_notified" -BATTERY_LEVEL=$(nomarchy-battery-remaining) -BATTERY_STATE=$(upower -i $(upower -e | grep 'BAT') | grep -E "state" | awk '{print $2}') - -send_notification() { - notify-send -u critical "󱐋 Time to recharge!" "Battery is down to ${1}%" -i battery-caution -t 30000 - nomarchy-hook battery-low "$1" -} - -if [[ -n $BATTERY_LEVEL && $BATTERY_LEVEL =~ ^[0-9]+$ ]]; then - if [[ $BATTERY_STATE == "discharging" ]] && (( BATTERY_LEVEL <= BATTERY_THRESHOLD )); then - if [[ ! -f $NOTIFICATION_FLAG ]]; then - send_notification $BATTERY_LEVEL - touch $NOTIFICATION_FLAG - fi - else - rm -f $NOTIFICATION_FLAG - fi -fi diff --git a/core/system/scripts/nomarchy-battery-remaining b/core/system/scripts/nomarchy-battery-remaining deleted file mode 100755 index 094b458..0000000 --- a/core/system/scripts/nomarchy-battery-remaining +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -# Returns the battery percentage remaining as an integer. -# Used by the battery monitor and the Ctrl + Shift + Super + B hotkey. - -upower -i $(upower -e | grep BAT) | awk '/percentage/ { - print int($2) - exit -}' diff --git a/core/system/scripts/nomarchy-battery-remaining-time b/core/system/scripts/nomarchy-battery-remaining-time deleted file mode 100755 index 62a2f80..0000000 --- a/core/system/scripts/nomarchy-battery-remaining-time +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -set -e - -# Returns the battery time remaining (to empty or full) in a compact format. - -battery_info=$(upower -i $(upower -e | grep BAT)) - -echo "$battery_info" | awk '/time to (empty|full)/ { - value = $4 - unit = $5 - if (unit == "minutes") { - hours = int(value / 60) - minutes = int(value % 60) - } else { - hours = int(value) - minutes = int((value - hours) * 60) - } - if (hours > 0 && minutes > 0) { - printf "%dh %dm", hours, minutes - } else if (hours > 0) { - printf "%dh", hours - } else { - printf "%dm", minutes - } - exit -}' diff --git a/core/system/scripts/nomarchy-battery-status b/core/system/scripts/nomarchy-battery-status deleted file mode 100755 index 8465e4d..0000000 --- a/core/system/scripts/nomarchy-battery-status +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -e - -# Returns a formatted battery status string with percentage and power draw/charge. -# Used by the battery notification hotkey (Ctrl + Shift + Super + B). - -battery_info=$(upower -i $(upower -e | grep BAT)) - -percentage=$(echo "$battery_info" | awk '/percentage/ { - print int($2) - exit -}') - -power_rate=$(echo "$battery_info" | awk '/energy-rate/ { - rounded = sprintf("%.1f", $2) - sub(/\.0$/, "", rounded) - print rounded - exit -}') - -state=$(echo "$battery_info" | awk '/state/ { print $2; exit }') -time_remaining=$(nomarchy-battery-remaining-time) -capacity=$(nomarchy-battery-capacity) - -if [[ $state == "charging" ]]; then - echo "󰁹 Battery ${percentage}% · ${time_remaining} to full ·  ${power_rate}W / ${capacity}Wh" -else - echo "󰁹 Battery ${percentage}% · ${time_remaining} left ·  ${power_rate}W / ${capacity}Wh" -fi diff --git a/core/system/scripts/nomarchy-brightness-display b/core/system/scripts/nomarchy-brightness-display deleted file mode 100755 index ebe05e6..0000000 --- a/core/system/scripts/nomarchy-brightness-display +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -e - -# Adjust brightness on the most likely display device. -# Usage: nomarchy-brightness-display <step> - -step="${1:-+5%}" - -# Start with the first possible output, then refine to the most likely given an order heuristic. -device="$(ls -1 /sys/class/backlight 2>/dev/null | head -n1)" -for candidate in amdgpu_bl* intel_backlight acpi_video*; do - if [[ -e /sys/class/backlight/$candidate ]]; then - device="$candidate" - break - fi -done - -# Set the actual brightness of the display device. -brightnessctl -d "$device" set "$step" >/dev/null - -# Use SwayOSD to display the new brightness setting. -nomarchy-swayosd-brightness "$(brightnessctl -d "$device" -m | cut -d',' -f4 | tr -d '%')" diff --git a/core/system/scripts/nomarchy-brightness-display-apple b/core/system/scripts/nomarchy-brightness-display-apple deleted file mode 100755 index bee7a67..0000000 --- a/core/system/scripts/nomarchy-brightness-display-apple +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -e - -# Adjust the brightness on Apple Studio Displays and Apple XDR Displays using asdcontrol. - -if (( $# == 0 )); then - echo "Adjust Apple Display Brightness by passing +5000 or -5000 (or any range from 0-60000)" -else - device="$(sudo asdcontrol --detect /dev/usb/hiddev* | grep ^/dev/usb/hiddev | cut -d: -f1)" - sudo asdcontrol "$device" -- "$1" >/dev/null - value="$(sudo asdcontrol "$device" | awk -F= '/BRIGHTNESS=/{print $2+0}')" - nomarchy-swayosd-brightness "$(( value * 100 / 60000 ))" -fi diff --git a/core/system/scripts/nomarchy-brightness-keyboard b/core/system/scripts/nomarchy-brightness-keyboard deleted file mode 100755 index 7bc56ea..0000000 --- a/core/system/scripts/nomarchy-brightness-keyboard +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -set -e - -# Adjust keyboard backlight brightness using available steps. -# Usage: nomarchy-brightness-keyboard <up|down|cycle> - -direction="${1:-up}" - -# Find keyboard backlight device (look for *kbd_backlight* pattern in leds class). -device="" -for candidate in /sys/class/leds/*kbd_backlight*; do - if [[ -e $candidate ]]; then - device="$(basename "$candidate")" - break - fi -done - -if [[ -z $device ]]; then - echo "No keyboard backlight device found" >&2 - exit 1 -fi - -# Get current and max brightness to determine step size. -max_brightness="$(brightnessctl -d "$device" max)" -current_brightness="$(brightnessctl -d "$device" get)" - -# Calculate step as one unit (keyboards typically have discrete levels like 0-3). -if [[ $direction == "cycle" ]]; then - new_brightness=$(( (current_brightness + 1) % (max_brightness + 1) )) -elif [[ $direction == "up" ]]; then - new_brightness=$((current_brightness + 1)) - (( new_brightness > max_brightness )) && new_brightness=$max_brightness -else - new_brightness=$((current_brightness - 1)) - (( new_brightness < 0 )) && new_brightness=0 -fi - -# Set the new brightness. -brightnessctl -d "$device" set "$new_brightness" >/dev/null - -# Use SwayOSD to display the new brightness setting. -percent=$((new_brightness * 100 / max_brightness)) -nomarchy-swayosd-kbd-brightness "$percent" diff --git a/core/system/scripts/nomarchy-haptic-touchpad b/core/system/scripts/nomarchy-haptic-touchpad deleted file mode 100755 index f800f0d..0000000 --- a/core/system/scripts/nomarchy-haptic-touchpad +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 - -"""Haptic feedback daemon for Synaptics touchpads with Manual Trigger. - -Monitors touchpad button press events and sends haptic pulses via HID -feature reports. Required because the kernel's HID haptic subsystem only -supports Auto Trigger with waveform enumeration, not the simpler Manual -Trigger protocol used by these Synaptics touchpads. -""" - -import fcntl, glob, os, struct, sys - -VENDOR = "06CB" -PRODUCT = "D01A" -REPORT_ID = 0x37 -INTENSITY = 40 # 0-100 - -# input_event: struct timeval (16 bytes on 64-bit) + type(H) + code(H) + value(i) -EVENT_FORMAT = "llHHi" -EVENT_SIZE = struct.calcsize(EVENT_FORMAT) -EV_KEY = 0x01 -BTN_LEFT = 272 -BTN_RIGHT = 273 -BTN_MIDDLE = 274 - -# ioctl: HIDIOCSFEATURE(len) = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) -def HIDIOCSFEATURE(length): - return 0xC0000000 | (length << 16) | (ord("H") << 8) | 0x06 - - -def find_hidraw(): - for path in sorted(glob.glob("/sys/class/hidraw/hidraw*")): - uevent = os.path.join(path, "device", "uevent") - try: - with open(uevent) as f: - content = f.read().upper() - if f"0000{VENDOR}" in content and f"0000{PRODUCT}" in content: - return os.path.join("/dev", os.path.basename(path)) - except OSError: - continue - return None - - -def find_touchpad_event(): - for path in sorted(glob.glob("/sys/class/input/event*/device/name")): - try: - with open(path) as f: - name = f.read().strip().upper() - if VENDOR in name and PRODUCT in name and "TOUCHPAD" in name: - event = path.split("/")[-3] - return os.path.join("/dev/input", event) - except OSError: - continue - return None - - -def main(): - hidraw = find_hidraw() - if not hidraw: - print("No Synaptics haptic touchpad hidraw device found", file=sys.stderr) - sys.exit(1) - - event = find_touchpad_event() - if not event: - print("No Synaptics haptic touchpad input device found", file=sys.stderr) - sys.exit(1) - - print(f"Haptic touchpad: hidraw={hidraw} input={event} intensity={INTENSITY}", flush=True) - - haptic_report = struct.pack("BB", REPORT_ID, INTENSITY) - ioctl_req = HIDIOCSFEATURE(len(haptic_report)) - - hidraw_fd = os.open(hidraw, os.O_RDWR) - event_fd = os.open(event, os.O_RDONLY) - - try: - while True: - data = os.read(event_fd, EVENT_SIZE) - if len(data) < EVENT_SIZE: - continue - _, _, ev_type, code, value = struct.unpack(EVENT_FORMAT, data) - if ev_type == EV_KEY and code in (BTN_LEFT, BTN_RIGHT, BTN_MIDDLE) and value == 1: - try: - fcntl.ioctl(hidraw_fd, ioctl_req, haptic_report) - except OSError: - pass - except KeyboardInterrupt: - pass - finally: - os.close(event_fd) - os.close(hidraw_fd) - - -if __name__ == "__main__": - main() diff --git a/core/system/scripts/nomarchy-hibernation-available b/core/system/scripts/nomarchy-hibernation-available deleted file mode 100755 index e5afade..0000000 --- a/core/system/scripts/nomarchy-hibernation-available +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e - -# Check if hibernation is supported -if [[ ! -f /sys/power/image_size ]]; then - exit 1 -fi - -# Sum all swap sizes (excluding zram) -SWAPSIZE_KB=$(awk '!/Filename|zram/ {sum += $3} END {print sum+0}' /proc/swaps) -SWAPSIZE=$(( 1024 * ${SWAPSIZE_KB:-0} )) - -HIBERNATION_IMAGE_SIZE=$(cat /sys/power/image_size) - -if (( SWAPSIZE > HIBERNATION_IMAGE_SIZE )) && [[ -f /etc/mkinitcpio.conf.d/nomarchy_resume.conf ]]; then - exit 0 -else - exit 1 -fi diff --git a/core/system/scripts/nomarchy-hibernation-remove b/core/system/scripts/nomarchy-hibernation-remove deleted file mode 100755 index 95e5838..0000000 --- a/core/system/scripts/nomarchy-hibernation-remove +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -set -e - -# Removes hibernation setup: disables swap, removes swapfile, removes fstab entry, -# removes resume hook, and removes suspend-then-hibernate configuration. - -MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/nomarchy_resume.conf" - -# Check if hibernation is configured -if [[ ! -f $MKINITCPIO_CONF ]] || ! grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then - echo "Hibernation is not set up" - exit 0 -fi - -if ! gum confirm "Remove hibernation setup?"; then - exit 0 -fi - -SWAP_SUBVOLUME="/swap" -SWAP_FILE="$SWAP_SUBVOLUME/swapfile" - -# Disable swap if active -if swapon --show | grep -q "$SWAP_FILE"; then - echo "Disabling swap on $SWAP_FILE" - sudo swapoff "$SWAP_FILE" -fi - -# Remove swapfile -if [[ -f $SWAP_FILE ]]; then - echo "Removing swapfile" - sudo rm "$SWAP_FILE" -fi - -# Remove swap subvolume -if sudo btrfs subvolume show "$SWAP_SUBVOLUME" &>/dev/null; then - echo "Removing Btrfs subvolume $SWAP_SUBVOLUME" - sudo btrfs subvolume delete "$SWAP_SUBVOLUME" -fi - -# Remove fstab entry -if grep -Fq "$SWAP_FILE" /etc/fstab; then - echo "Removing swapfile from /etc/fstab" - sudo cp -a /etc/fstab "/etc/fstab.$(date +%Y%m%d%H%M%S).back" - sudo sed -i "\|$SWAP_FILE|d" /etc/fstab - sudo sed -i '/^# Btrfs swapfile for system hibernation$/d' /etc/fstab -fi - -# Remove suspend-then-hibernate configuration -echo "Removing suspend-then-hibernate configuration" -sudo rm -f /etc/systemd/logind.conf.d/lid.conf -sudo rm -f /etc/systemd/sleep.conf.d/hibernate.conf - -# Remove mkinitcpio resume hook -echo "Removing resume hook" -sudo rm "$MKINITCPIO_CONF" - -echo "Regenerating initramfs..." -sudo limine-mkinitcpio - -echo "Hibernation removed" diff --git a/core/system/scripts/nomarchy-hibernation-setup b/core/system/scripts/nomarchy-hibernation-setup deleted file mode 100755 index 325ccdc..0000000 --- a/core/system/scripts/nomarchy-hibernation-setup +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -set -e - -# Creates a swap file in the btrfs subvolume, adds the swap file to /etc/fstab, -# adds a resume hook to mkinitcpio, and configures suspend-then-hibernate. - -if [[ ! -f /sys/power/image_size ]]; then - echo -e "Hibernation is not supported on your system" >&2 - exit 0 -fi - -if ! command -v limine-mkinitcpio &>/dev/null; then - echo "Skipping hibernation setup (requires Limine bootloader)" - exit 0 -fi - -MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/nomarchy_resume.conf" - -# Check if hibernation is already configured -if [[ -f $MKINITCPIO_CONF ]] && grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then - echo "Hibernation is already set up" - exit 0 -fi - -if [[ $1 != "--force" ]]; then - MEM_TOTAL_HUMAN=$(free --human | awk '/Mem/ {print $2}') - if ! gum confirm "Use $MEM_TOTAL_HUMAN on boot drive to make hibernation available?"; then - exit 0 - fi -fi - -SWAP_SUBVOLUME="/swap" -SWAP_FILE="$SWAP_SUBVOLUME/swapfile" - -# Create btrfs subvolume for swap -if ! sudo btrfs subvolume show "$SWAP_SUBVOLUME" &>/dev/null; then - echo "Creating Btrfs subvolume" - sudo btrfs subvolume create "$SWAP_SUBVOLUME" - sudo chattr +C "$SWAP_SUBVOLUME" -fi - -# Create swapfile -if ! sudo swaplabel "$SWAP_FILE" &>/dev/null; then - echo "Creating swapfile in Btrfs subvolume" - MEM_TOTAL_KB="$(awk '/MemTotal/ {print $2}' /proc/meminfo)k" - sudo btrfs filesystem mkswapfile -s "$MEM_TOTAL_KB" "$SWAP_FILE" -fi - -# Add swapfile to fstab -if ! grep -Fq "$SWAP_FILE" /etc/fstab; then - echo "Adding swapfile to /etc/fstab" - sudo cp -a /etc/fstab "/etc/fstab.$(date +%Y%m%d%H%M%S).back" - printf "\n# Btrfs swapfile for system hibernation\n%s none swap defaults,pri=0 0 0\n" "$SWAP_FILE" | sudo tee -a /etc/fstab >/dev/null -fi - -# Enable swap -if ! swapon --show | grep -q "$SWAP_FILE"; then - echo "Enabling swap on $SWAP_FILE" - sudo swapon -p 0 "$SWAP_FILE" -fi - -# Add resume hook to mkinitcpio -sudo mkdir -p /etc/mkinitcpio.conf.d -echo "Adding resume hook to $MKINITCPIO_CONF" -echo "HOOKS+=(resume)" | sudo tee "$MKINITCPIO_CONF" >/dev/null - -# Add resume= kernel parameters so the initramfs resume hook knows where to find the -# hibernation image. Without these, resume happens late (after GPU drivers load) and fails. -RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf" -if [[ ! -f $RESUME_DROP_IN ]]; then - echo "Adding resume kernel parameters" - sudo swapon -p 0 "$SWAP_FILE" 2>/dev/null - RESUME_DEVICE=$(findmnt -no SOURCE -T "$SWAP_FILE" | sed 's/\[.*\]//') - RESUME_OFFSET=$(sudo btrfs inspect-internal map-swapfile -r "$SWAP_FILE") - sudo mkdir -p /etc/limine-entry-tool.d - echo "KERNEL_CMDLINE[default]+=\" resume=$RESUME_DEVICE resume_offset=$RESUME_OFFSET\"" | sudo tee "$RESUME_DROP_IN" >/dev/null - sudo tee -a /etc/default/limine < "$RESUME_DROP_IN" >/dev/null -fi - -# Use ACPI alarm for RTC wakeup on s2idle systems (needed for suspend-then-hibernate) -if grep -q "\[s2idle\]" /sys/power/mem_sleep 2>/dev/null; then - LIMINE_DROP_IN="/etc/limine-entry-tool.d/rtc-alarm.conf" - if [[ ! -f $LIMINE_DROP_IN ]]; then - echo "Enabling ACPI RTC alarm for s2idle suspend" - sudo mkdir -p /etc/limine-entry-tool.d - echo 'KERNEL_CMDLINE[default]+=" rtc_cmos.use_acpi_alarm=1"' | sudo tee "$LIMINE_DROP_IN" >/dev/null - sudo tee -a /etc/default/limine < "$LIMINE_DROP_IN" >/dev/null - fi -fi - -# Regenerate initramfs and boot entry -echo "Regenerating initramfs..." -sudo limine-mkinitcpio -sudo limine-update - -echo - -if [[ $1 != "--force" ]] && gum confirm "Reboot to enable hibernation?"; then - nomarchy-system-reboot -fi diff --git a/core/system/scripts/nomarchy-hw-asus-rog b/core/system/scripts/nomarchy-hw-asus-rog deleted file mode 100755 index 11ab5e6..0000000 --- a/core/system/scripts/nomarchy-hw-asus-rog +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e - -# Detect whether the computer is an Asus ROG machine. - -[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "ASUSTeK COMPUTER INC." ]] && - grep -q "ROG" /sys/class/dmi/id/product_family 2>/dev/null diff --git a/core/system/scripts/nomarchy-hw-match b/core/system/scripts/nomarchy-hw-match deleted file mode 100755 index 8262bbd..0000000 --- a/core/system/scripts/nomarchy-hw-match +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e - -# Match against the computer's DMI product name (case-insensitive). -# Usage: nomarchy-hw-match "XPS" - -grep -qi "$1" /sys/class/dmi/id/product_name 2>/dev/null diff --git a/core/system/scripts/nomarchy-hw-vulkan b/core/system/scripts/nomarchy-hw-vulkan deleted file mode 100755 index a3783c9..0000000 --- a/core/system/scripts/nomarchy-hw-vulkan +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e - -# Detect whether Vulkan is available. - -[[ -d /usr/share/vulkan/icd.d ]] && - find /usr/share/vulkan/icd.d -maxdepth 1 -name "*.json" -print -quit | grep -q . diff --git a/core/system/scripts/nomarchy-powerprofiles-list b/core/system/scripts/nomarchy-powerprofiles-list deleted file mode 100755 index 8515820..0000000 --- a/core/system/scripts/nomarchy-powerprofiles-list +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -# Returns a list of all the available power profiles on the system. -# Used by the Nomarchy Menu under Setup > Power Profile. - -powerprofilesctl list | - awk '/^\s*[* ]\s*[a-zA-Z0-9\-]+:$/ { gsub(/^[*[:space:]]+|:$/,""); print }' | - tac diff --git a/core/system/scripts/nomarchy-restart-bluetooth b/core/system/scripts/nomarchy-restart-bluetooth deleted file mode 100755 index 44c33f0..0000000 --- a/core/system/scripts/nomarchy-restart-bluetooth +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Unblock and restart the bluetooth service. - -echo -e "Unblocking bluetooth...\n" -rfkill unblock bluetooth -rfkill list bluetooth diff --git a/core/system/scripts/nomarchy-restart-pipewire b/core/system/scripts/nomarchy-restart-pipewire deleted file mode 100755 index 756baeb..0000000 --- a/core/system/scripts/nomarchy-restart-pipewire +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e - -# Restart the PipeWire audio service to fix audio issues or apply new configuration. - -echo -e "Restarting pipewire audio service...\n" -systemctl --user restart pipewire.service diff --git a/core/system/scripts/nomarchy-restart-trackpad b/core/system/scripts/nomarchy-restart-trackpad deleted file mode 100755 index 6642f7e..0000000 --- a/core/system/scripts/nomarchy-restart-trackpad +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -# Reload the intel_quicki2c driver to fix a dead trackpad. -# The THC (Touch Host Controller) can fail to initialize interrupts -# during boot or after suspend, leaving the trackpad registered but -# not delivering events. - -sudo modprobe -r intel_quicki2c && sudo modprobe intel_quicki2c diff --git a/core/system/scripts/nomarchy-restart-wifi b/core/system/scripts/nomarchy-restart-wifi deleted file mode 100755 index 160a50d..0000000 --- a/core/system/scripts/nomarchy-restart-wifi +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Unblock and restart the Wi-Fi service. - -echo -e "Unblocking wifi...\n" -rfkill unblock wifi -rfkill list wifi diff --git a/core/system/scripts/nomarchy-restart-xcompose b/core/system/scripts/nomarchy-restart-xcompose deleted file mode 100755 index 2099af3..0000000 --- a/core/system/scripts/nomarchy-restart-xcompose +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Restart the XCompose input method service (fcitx5) to apply new compose key settings. - -nomarchy-restart-app fcitx5 --disable notificationitem diff --git a/core/system/scripts/nomarchy-setup-dns b/core/system/scripts/nomarchy-setup-dns deleted file mode 100755 index a59d207..0000000 --- a/core/system/scripts/nomarchy-setup-dns +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Configure DNS declaratively for Nomarchy NixOS. -# Hybrid: updates /etc/nixos/state.json and runs sys-update. - -STATE_FILE="/etc/nixos/state.json" - -if [[ -z $1 ]]; then - dns=$(gum choose --height 6 --header "Select DNS provider" Cloudflare Google DHCP Custom) -else - dns=$1 -fi - -case "$dns" in -Cloudflare|Google|DHCP) - tmp=$(mktemp); sudo jq --arg dns "$dns" '.dns = $dns' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" - ;; - -Custom) - echo "Enter your DNS servers (space-separated, e.g. '192.168.1.1 1.1.1.1'):" - read -r dns_servers - - if [[ -z $dns_servers ]]; then - echo "Error: No DNS servers provided." - exit 1 - fi - - # Convert to JSON array safely - dns_array=$(echo "$dns_servers" | jq -R 'split(" ")') - tmp=$(mktemp); sudo jq --arg dns "Custom" --argjson servers "$dns_array" '.dns = $dns | .customDns = $servers' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" - ;; -esac - -echo "DNS configured to $dns. Applying changes..." -sudo nomarchy-sys-update diff --git a/core/system/scripts/nomarchy-setup-fido2 b/core/system/scripts/nomarchy-setup-fido2 deleted file mode 100755 index 6fc26f3..0000000 --- a/core/system/scripts/nomarchy-setup-fido2 +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Configure FIDO2 support declaratively for Nomarchy NixOS. - -STATE_FILE="/etc/nixos/state.json" - -if [[ "--remove" == $1 ]]; then - tmp=$(mktemp); sudo jq '.features.fido2 = false' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" - echo "FIDO2 support disabled. Applying changes..." - sudo nomarchy-sys-update - exit 0 -fi - -tmp=$(mktemp); sudo jq '.features.fido2 = true' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" -echo "FIDO2 support enabled. Applying changes..." -sudo nomarchy-sys-update - -# Enrollment is still an imperative action -if command -v pamu2fcfg &> /dev/null; then - echo "Let's register your FIDO2 key now." - mkdir -p ~/.config/Yubico - pamu2fcfg > ~/.config/Yubico/u2f_keys - echo "FIDO2 key registered." -else - echo "pamu2fcfg not found. It will be available after the next reboot or sys-update." -fi diff --git a/core/system/scripts/nomarchy-setup-fingerprint b/core/system/scripts/nomarchy-setup-fingerprint deleted file mode 100755 index 6cf81cc..0000000 --- a/core/system/scripts/nomarchy-setup-fingerprint +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Configure fingerprint support declaratively for Nomarchy NixOS. - -STATE_FILE="/etc/nixos/state.json" - -if [[ "--remove" == $1 ]]; then - tmp=$(mktemp); sudo jq '.features.fingerprint = false' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" - echo "Fingerprint support disabled. Applying changes..." - sudo nomarchy-sys-update - exit 0 -fi - -tmp=$(mktemp); sudo jq '.features.fingerprint = true' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" -echo "Fingerprint support enabled. Applying changes..." -sudo nomarchy-sys-update - -# Enrollment is still an imperative action -if command -v fprintd-enroll &> /dev/null; then - echo "Let's enroll your fingerprint now." - fprintd-enroll - echo "Fingerprint enrolled." -else - echo "fprintd not found. It will be available after the next reboot or sys-update." -fi diff --git a/core/system/scripts/nomarchy-sudo-passwordless-toggle b/core/system/scripts/nomarchy-sudo-passwordless-toggle deleted file mode 100755 index 4290933..0000000 --- a/core/system/scripts/nomarchy-sudo-passwordless-toggle +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -set -e - -# Toggle passwordless sudo for the current user. -# First run: enables passwordless sudo for 15 minutes (after confirmation). -# Second run: disables it early. - -NOPASSWD_FILE="/etc/sudoers.d/99-nomarchy-nopasswd-${USER}" -TIMER_NAME="nomarchy-nopasswd-expire-${USER}" - -# Safety: if the file exists but the timer doesn't (e.g. after reboot), clean up -if sudo test -f "$NOPASSWD_FILE" && ! systemctl is-active "${TIMER_NAME}.timer" &>/dev/null; then - sudo rm "$NOPASSWD_FILE" -fi - -# Check for the file directly — sudo -n can stay cached or be granted by other rules -if sudo test -f "$NOPASSWD_FILE"; then - sudo rm "$NOPASSWD_FILE" - sudo systemctl stop "${TIMER_NAME}.timer" 2>/dev/null - echo "Passwordless sudo has been DISABLED. Sudo will require a password again." -else - echo "" - echo "⚠️ WARNING: This will allow ANY process running as your user to" - echo "execute ANY command as root WITHOUT a password for 15 minutes." - echo "" - echo "This is useful for AI agents that need to run sudo commands," - echo "but it significantly weakens the security of your system." - echo "Anyone or anything with access to your user account gets full root." - echo "" - echo "Passwordless sudo will automatically disable after 15 minutes." - echo "Run this command again to disable it early." - echo "" - - if gum confirm "Enable passwordless sudo for 15 minutes? This is a significant security risk!"; then - echo "${USER} ALL=(ALL) NOPASSWD: ALL" | sudo tee "$NOPASSWD_FILE" > /dev/null - sudo chmod 440 "$NOPASSWD_FILE" - sudo systemd-run --on-active=15m --timer-property=AccuracySec=1s --unit="$TIMER_NAME" \ - rm "$NOPASSWD_FILE" - echo "Passwordless sudo has been ENABLED. It will automatically disable in 15 minutes." - echo "Note: if you restart before then, run nomarchy-sudo-passwordless-toggle again to disable it." - else - echo "Aborted. No changes made." - fi -fi diff --git a/core/system/scripts/nomarchy-sudo-reset b/core/system/scripts/nomarchy-sudo-reset deleted file mode 100755 index 5385567..0000000 --- a/core/system/scripts/nomarchy-sudo-reset +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Reset the sudo lockout/faillock for the current user. -# This clears any failed authentication attempts that may have locked the user out. - -# Resetting sudo lockout for user -su -c "faillock --reset --user $USER" diff --git a/core/system/scripts/nomarchy-swayosd-brightness b/core/system/scripts/nomarchy-swayosd-brightness deleted file mode 100755 index bf614e8..0000000 --- a/core/system/scripts/nomarchy-swayosd-brightness +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -# Display brightness level using SwayOSD on the current monitor. -# Usage: nomarchy-swayosd-brightness <percent> - -percent="$1" - -progress="$(awk -v p="$percent" 'BEGIN{printf "%.2f", p/100}')" -[[ $progress == "0.00" ]] && progress="0.01" - -swayosd-client \ - --monitor "$(hyprctl monitors -j | jq -r '.[]|select(.focused==true).name')" \ - --custom-icon display-brightness \ - --custom-progress "$progress" \ - --custom-progress-text "${percent}%" diff --git a/core/system/scripts/nomarchy-swayosd-kbd-brightness b/core/system/scripts/nomarchy-swayosd-kbd-brightness deleted file mode 100755 index 4b840b3..0000000 --- a/core/system/scripts/nomarchy-swayosd-kbd-brightness +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -# Display keyboard brightness level using SwayOSD on the current monitor. -# Usage: nomarchy-swayosd-kbd-brightness <percent> - -percent="$1" - -progress="$(awk -v p="$percent" 'BEGIN{printf "%.2f", p/100}')" -[[ $progress == "0.00" ]] && progress="0.01" - -swayosd-client \ - --monitor "$(hyprctl monitors -j | jq -r '.[]|select(.focused==true).name')" \ - --custom-icon keyboard-brightness \ - --custom-progress "$progress" \ - --custom-progress-text "${percent}%" diff --git a/core/system/scripts/nomarchy-system-logout b/core/system/scripts/nomarchy-system-logout deleted file mode 100755 index 1d8d775..0000000 --- a/core/system/scripts/nomarchy-system-logout +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -# Logout command that first closes all application windows (thus giving them a chance to save state), -# then stops the session, returning to the SDDM login screen. - -# Schedule the session stop after closing windows (detached from terminal) -nohup bash -c "sleep 2 && uwsm stop" >/dev/null 2>&1 & - -# Now close all windows -nomarchy-hyprland-window-close-all -sleep 1 # Allow apps like Chrome to shutdown correctly diff --git a/core/system/scripts/nomarchy-system-reboot b/core/system/scripts/nomarchy-system-reboot deleted file mode 100755 index 02ed9d5..0000000 --- a/core/system/scripts/nomarchy-system-reboot +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -e - -# Reboot command that first closes all application windows (thus giving them a chance to save state). -# This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly. - -nomarchy-state clear re*-required - -# Schedule the reboot to happen after closing windows (detached from terminal) -nohup bash -c "sleep 2 && systemctl reboot --no-wall" >/dev/null 2>&1 & - -# Now close all windows -nomarchy-hyprland-window-close-all -sleep 1 # Allow apps like Chrome to shutdown correctly diff --git a/core/system/scripts/nomarchy-system-shutdown b/core/system/scripts/nomarchy-system-shutdown deleted file mode 100755 index 160db44..0000000 --- a/core/system/scripts/nomarchy-system-shutdown +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -e - -# Shutdown command that first closes all application windows (thus giving them a chance to save state). -# This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly. - -nomarchy-state clear re*-required - -# Schedule the shutdown to happen after closing windows (detached from terminal) -nohup bash -c "sleep 2 && systemctl poweroff --no-wall" >/dev/null 2>&1 & - -# Now close all windows -nomarchy-hyprland-window-close-all -sleep 1 # Allow apps like Chrome to shutdown correctly diff --git a/core/system/scripts/nomarchy-toggle-hybrid-gpu b/core/system/scripts/nomarchy-toggle-hybrid-gpu deleted file mode 100755 index 6ad0998..0000000 --- a/core/system/scripts/nomarchy-toggle-hybrid-gpu +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14). -# Declarative enablement + Runtime mode switching for Nomarchy NixOS. - -STATE_FILE="/etc/nixos/state.json" - -# Check if supergfxd is enabled in config -if [[ $(sudo jq -r '.features.hybridGPU // false' "$STATE_FILE") != "true" ]]; then - if gum confirm "Hybrid GPU support is not enabled. Enable it now? (Requires sys-update)"; then - tmp=$(mktemp); sudo jq '.features.hybridGPU = true' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" - echo "Hybrid GPU support enabled in configuration. Applying changes..." - sudo nomarchy-sys-update - echo "Please run this command again after the update." - exit 0 - fi - exit 1 -fi - -if ! command -v supergfxctl &> /dev/null; then - echo "supergfxctl not found. Is the system updated?" - exit 1 -fi - -gpu_mode=$(supergfxctl -g) -echo "Current GPU mode: $gpu_mode" - -case "$gpu_mode" in -"Integrated") - if gum confirm "Switch to Hybrid mode (enables dGPU) and reboot?"; then - supergfxctl -m Hybrid - echo "Switching to Hybrid mode..." - nomarchy-system-reboot - fi - ;; -"Hybrid") - if gum confirm "Switch to Integrated mode (disables dGPU) and reboot?"; then - supergfxctl -m Integrated - echo "Switching to Integrated mode..." - nomarchy-system-reboot - fi - ;; -*) - echo "Hybrid GPU in unknown mode: $gpu_mode. Try 'supergfxctl -m Hybrid' manually." - exit 1 - ;; -esac diff --git a/core/system/scripts/nomarchy-toggle-idle b/core/system/scripts/nomarchy-toggle-idle deleted file mode 100755 index 47a2320..0000000 --- a/core/system/scripts/nomarchy-toggle-idle +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Toggles the idle daemon (hypridle) between enabled and disabled. -# Hybrid: updates state.json and provides instant feedback. - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" -mkdir -p "$STATE_DIR" - -# Initialize if doesn't exist -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -if [[ $NOMARCHY_TOGGLE_IDLE == "false" ]]; then - NEW_VALUE="true" - setsid hypridle >/dev/null 2>&1 & - notify-send -u low " Now locking computer when idle" -else - NEW_VALUE="false" - pkill -x hypridle - notify-send -u low " Stop locking computer when idle" -fi - -nomarchy-state-write idle "$NEW_VALUE" --type bool - -echo "Idle state set to $NEW_VALUE. Environment will be fully updated on next rebuild." - -pkill -RTMIN+9 waybar # Signal waybar if needed diff --git a/core/system/scripts/nomarchy-toggle-suspend b/core/system/scripts/nomarchy-toggle-suspend deleted file mode 100755 index bddb746..0000000 --- a/core/system/scripts/nomarchy-toggle-suspend +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Execute system suspend -systemctl suspend diff --git a/core/system/scripts/nomarchy-tz-select b/core/system/scripts/nomarchy-tz-select deleted file mode 100755 index b92f0aa..0000000 --- a/core/system/scripts/nomarchy-tz-select +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Select system timezone declaratively for Nomarchy NixOS. - -STATE_FILE="/etc/nixos/state.json" - -timezone=$(timedatectl list-timezones | gum filter --height 20 --header "Set timezone") || exit 1 - -tmp=$(mktemp); sudo jq --arg tz "$timezone" '.timezone = $tz' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" - -echo "Timezone is now set to $timezone. Applying changes..." -sudo nomarchy-sys-update diff --git a/core/system/scripts/nomarchy-update-time b/core/system/scripts/nomarchy-update-time deleted file mode 100755 index 8eb66ae..0000000 --- a/core/system/scripts/nomarchy-update-time +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -e - -echo "Updating time..." -sudo systemctl restart systemd-timesyncd diff --git a/core/system/scripts/nomarchy-wifi-powersave b/core/system/scripts/nomarchy-wifi-powersave deleted file mode 100755 index 4a5073e..0000000 --- a/core/system/scripts/nomarchy-wifi-powersave +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Toggles wifi power saving declaratively. -# Usage: nomarchy-wifi-powersave <on|off> - -STATE_FILE="/etc/nixos/state.json" - -case "$1" in -on) value="true" ;; -off) value="false" ;; -*) echo "Usage: nomarchy-wifi-powersave <on|off>"; exit 1 ;; -esac - -tmp=$(mktemp); sudo jq --argjson val "$value" '.wifi.powersave = $val' "$STATE_FILE" > "$tmp" && sudo mv "$tmp" "$STATE_FILE" - -echo "Wifi powersave set to $1. Applying changes..." -sudo nomarchy-sys-update diff --git a/core/system/session.nix b/core/system/session.nix deleted file mode 100644 index e4a3265..0000000 --- a/core/system/session.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ config, lib, pkgs, ... }: - -# uwsm + Hyprland session manager wiring. Present on every Nomarchy install -# regardless of any optional toggles — Hyprland is launched via uwsm so -# it inherits a proper systemd graphical-session.target (which user services -# like nomarchy-wallpaper, walker, and elephant chain off). -# -# Lived in core/system/virtualization.nix until 2026-05-22 by historical -# accident; the placement had nothing to do with libvirt/docker. - -{ - programs.uwsm = { - enable = lib.mkDefault true; - waylandCompositors.hyprland = { - binPath = "/run/current-system/sw/bin/Hyprland"; - prettyName = "Hyprland"; - }; - }; -} diff --git a/core/system/snapper.nix b/core/system/snapper.nix deleted file mode 100644 index 95b93ca..0000000 --- a/core/system/snapper.nix +++ /dev/null @@ -1,42 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.nomarchy.system.snapper; - rootIsBtrfs = (config.fileSystems."/".fsType or "") == "btrfs"; - active = cfg.enable && rootIsBtrfs; -in -{ - config = lib.mkIf active { - # `nixos-rebuild-snap`: take a Snapper pre-rebuild snapshot, then run - # `nixos-rebuild switch` against the current host. The hostname is read - # from the running config so this script works on every machine without - # editing. - environment.systemPackages = [ - (pkgs.writeShellScriptBin "nixos-rebuild-snap" '' - if [ "$(id -u)" -ne 0 ]; then - echo "This script must be run as root (use sudo)" >&2 - exit 1 - fi - echo "Creating pre-rebuild snapshot..." - ${pkgs.snapper}/bin/snapper -c root create \ - -d "Pre-rebuild $(date +'%Y-%m-%d %H:%M:%S')" \ - --cleanup-algorithm number - echo "Rebuilding..." - nixos-rebuild switch --flake .#${config.networking.hostName} "$@" - '') - ]; - - services.snapper.configs = { - root = { - SUBVOLUME = "/"; - TIMELINE_CREATE = true; - TIMELINE_CLEANUP = true; - TIMELINE_LIMIT_HOURLY = "5"; - TIMELINE_LIMIT_DAILY = "7"; - TIMELINE_LIMIT_WEEKLY = "0"; - TIMELINE_LIMIT_MONTHLY = "0"; - TIMELINE_LIMIT_YEARLY = "0"; - }; - }; - }; -} diff --git a/core/system/state.nix b/core/system/state.nix deleted file mode 100644 index 6bc4bd9..0000000 --- a/core/system/state.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ lib, ... }: - -let - nomarchyLib = import ../../lib { inherit lib; }; - # Same canonical schema as core/home/state.nix and the options.nix - # files — keeps every state default in one place. - schema = import ../../lib/state-schema.nix { inherit lib; }; - systemState = nomarchyLib.readSystemState; -in -{ - # Every assignment is lib.mkDefault so a downstream /etc/nixos/system.nix - # can still set e.g. `nomarchy.system.features.hybridGPU = true;` - # without colliding with the default values. The Nix options are now the - # declarative source of truth. - config.nomarchy.system = { - }; -} diff --git a/core/system/systemd.nix b/core/system/systemd.nix deleted file mode 100644 index 510c9b7..0000000 --- a/core/system/systemd.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - systemd.settings.Manager.DefaultTimeoutStopSec = lib.mkDefault "5s"; - - systemd.services."user@".serviceConfig.TimeoutStopSec = lib.mkDefault "5s"; - - powerManagement.powerDownCommands = '' - # --- force-igpu --- - # Use the Vfio to Integrated trick to turn off NVIDIA dgpu when in integrated mode - if [[ $1 == "hibernate" ]] && ${pkgs.coreutils}/bin/lsmod | grep -q supergfxd; then - ${pkgs.supergfxctl}/bin/supergfxctl -m Vfio || true - sleep 1 - fi - - # --- keyboard-backlight --- - # Turn off keyboard backlight before hibernate to prevent hang on power-off. - if [[ $1 == "hibernate" ]]; then - device="" - for candidate in /sys/class/leds/*kbd_backlight*; do - if [[ -e "$candidate" ]]; then - device="$(${pkgs.coreutils}/bin/basename "$candidate")" - break - fi - done - if [[ -n "$device" ]]; then - ${pkgs.brightnessctl}/bin/brightnessctl -d "$device" set 0 >/dev/null 2>&1 || true - fi - fi - - # --- unmount-fuse --- - # Lazy-unmount gvfsd-fuse filesystems before suspend/hibernate - while IFS=' ' read -r _ mountpoint fstype _; do - if [[ $fstype == fuse.gvfsd-fuse ]]; then - mountpoint=$(printf '%b' "$mountpoint") - ${pkgs.fuse3}/bin/fusermount3 -uz "$mountpoint" 2>/dev/null || ${pkgs.fuse}/bin/fusermount -uz "$mountpoint" 2>/dev/null || true - fi - done < /proc/mounts - ''; - - powerManagement.resumeCommands = '' - # --- force-igpu --- - if ${pkgs.coreutils}/bin/lsmod | grep -q supergfxd; then - sleep 4 - ${pkgs.supergfxctl}/bin/supergfxctl -m Vfio || true - sleep 1 - ${pkgs.supergfxctl}/bin/supergfxctl -m Integrated || true - fi - - # --- unmount-fuse --- - ( - sleep 5 - for uid_dir in /run/user/*; do - uid="$(${pkgs.coreutils}/bin/basename "$uid_dir")" - if [[ -S $uid_dir/bus ]]; then - sudo -u "#$uid" env \ - DBUS_SESSION_BUS_ADDRESS="unix:path=$uid_dir/bus" \ - XDG_RUNTIME_DIR="$uid_dir" \ - systemctl --user restart gvfs-daemon.service 2>/dev/null || true - fi - done - ) & - ''; -} diff --git a/core/system/virtualization.nix b/core/system/virtualization.nix deleted file mode 100644 index 207265d..0000000 --- a/core/system/virtualization.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - libvirt = config.nomarchy.system.virtualization.libvirt.enable; - docker = config.nomarchy.system.virtualization.docker.enable; -in -{ - # Optional: libvirt + virt-manager + OVMF. Toggle with - # `nomarchy.system.virtualization.libvirt.enable = true;`. The user must - # be in the `libvirtd` group to drive virsh / virt-manager. - virtualisation.libvirtd.enable = lib.mkIf libvirt true; - - # Optional: Docker + docker-compose. - virtualisation.docker.enable = lib.mkIf docker true; - - environment.systemPackages = lib.mkMerge [ - (lib.mkIf libvirt (with pkgs; [ - virt-manager - qemu - OVMF - ])) - (lib.mkIf docker (with pkgs; [ - docker-compose - ])) - ]; -} diff --git a/core/system/vm-guest.nix b/core/system/vm-guest.nix deleted file mode 100644 index 5d95487..0000000 --- a/core/system/vm-guest.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ inputs, lib, pkgs, ... }: - -{ - # Home Manager activation is provided by the home-manager.nixosModules.home-manager - # module wired up in flake.nix. It runs as a systemd user service per user and - # deploys ~/.config/hypr, ~/.config/nomarchy, ~/.config/waybar, etc. before the - # graphical session starts. - - # Shared VM configuration - virtualisation.vmVariant = { - virtualisation.graphics = true; - virtualisation.qemu.options = [ "-device" "virtio-vga" ]; - }; - - # Improve VM resolution and guest experience - services.spice-vdagentd.enable = true; - services.qemuGuest.enable = true; - services.xserver.videoDrivers = [ "qxl" "virtio" "modesetting" ]; - - # Provide the flake to the VM for local rebuilding - environment.etc."nomarchy".source = lib.mkDefault inputs.self; - - # Dummy hardware config for VM - fileSystems."/" = lib.mkDefault { device = "/dev/disk/by-label/nixos"; }; - boot.loader.grub.device = lib.mkDefault "/dev/vda"; - boot.initrd.availableKernelModules = [ "virtio_pci" "virtio_blk" "virtio_gpu" "virtio_net" "virtio_mmio" ]; - - # Force early KMS for Plymouth - boot.initrd.kernelModules = [ "virtio_gpu" ]; -} diff --git a/docs/AGENT.md b/docs/AGENT.md deleted file mode 100644 index efc51aa..0000000 --- a/docs/AGENT.md +++ /dev/null @@ -1,261 +0,0 @@ -# Agent Instructions for Nomarchy - -You're an AI coding agent picking up the Nomarchy project. This document gives you everything you need to be useful from the first turn: what Nomarchy is, how it's organized, what the rules are, and how to actually pick up the next piece of work. - -If anything below conflicts with what the user just said, the user wins. If anything below conflicts with the existing code, read the code — these notes can rot. - ---- - -## 1. What Nomarchy is - -Nomarchy is a NixOS-based distribution that ships a highly curated Hyprland desktop on a strictly declarative, flake-based foundation. It targets power users who want a polished, reproducible desktop environment. - -Concretely: - -- A flake at the repo root exposes `nixosModules.system` (foundational OS modules) and `nixosModules.home` (apps + desktop), plus three `nixosConfigurations` (`nomarchy-installer`, `nomarchy-live`, `default`) and standalone `homeConfigurations`. -- Downstream users get the distro by importing `nomarchy.nixosModules.system` and `nomarchy.nixosModules.home` from their own `/etc/nixos/flake.nix`. The Nomarchy installer generates that flake for them; an existing-NixOS user can hand-write it (see `docs/MIGRATION.md`). -- A bash/`gum` TUI installer lives in `installer/install.sh` (~1100 lines). It auto-detects hardware via `installer/hardware-db.sh` (DMI + `lspci` + `BAT*` sysfs), prompts for the rest, and generates `flake.nix`, `system.nix`, `home.nix`, and `hardware-selection.nix` into `/mnt/etc/nixos/`. -- The desktop is Hyprland + waybar + walker + a curated theming engine (`themes/`) with 22 palettes wired through Stylix. - -Read in this order to come up to speed: - -1. `README.md` — public face. -2. `docs/STRUCTURE.md` — directory layout and module logic. -3. `docs/OPTIONS.md` — every `nomarchy.*` option a downstream flake can set. -4. `docs/ROADMAP.md` — what's planned. **This is your work queue.** -5. `docs/SCRIPTS.md` — the script & menu audit table (Pillar 3 of the roadmap). -6. `docs/MIGRATION.md` — how an existing NixOS install becomes Nomarchy. -7. `docs/creating-themes.md` — when palette work comes up. - ---- - -## 2. How the repo is organized - -``` -core/ Foundational OS + user defaults. Don't put apps here. - system/ NixOS modules, all imported via core/default.nix. - options.nix nomarchy.system.* options live here. - hardware.nix nomarchy.hardware.* options + module wiring. - network.nix NetworkManager, DNS, networkmanagerapplet. - impermanence.nix Erase-Your-Darlings root wipe. - scripts/ Low-level system scripts (battery, brightness, hardware). - home/ Home Manager modules. - options.nix Most home-side nomarchy.* options. - overrides.nix nomarchy.overrides.* (reserved; currently no-op — see ROADMAP). - config/ Plain dotfiles symlinked into ~/.config. - -features/ Apps and desktop components. Add new apps here. - apps/ One subdir per app (alacritty, btop, kitty, vscode.nix, …). - desktop/ hyprland, waybar, idle.nix, nightlight.nix, … - scripts/ User-PATH scripts, battery-monitor user service. - utils/ ~68 nomarchy-* user scripts. - default.nix Packages them as nomarchy-system-scripts derivation. - -themes/ Theme engine + 22 palettes. - engine/ Loader, Stylix glue, switcher, scripts (font, theme, wallpaper). - palettes/ One subdir per palette (summer-night, tokyo-night, …). - -hosts/ ISO host configs (nomarchy-installer, nomarchy-live/live-iso). -installer/ The bash/gum TUI + disko configs + hardware-db.sh. -lib/ Shared Nix helpers (state schema, color resolution, paths). -docs/ All long-form documentation. README.md stays at repo root. -bin/ Convenience wrappers for testing (nomarchy-test-installer, …). -``` - -When you add a new feature: - -- A new app → `features/apps/<name>/default.nix` (+ optional `config/`), import it from `features/default.nix`. **All app modules must be opt-in** via a `nomarchy.apps.<name>.enable` option defined in `core/home/options.nix`. Enabling the module should also handle the `programs.<name>.enable` or `home.packages` installation where applicable. -- A new system service → `core/system/<name>.nix`, import from `core/default.nix`. -- A new toggle → add to `core/system/options.nix` or `core/home/options.nix`, wire it into the relevant module, document it in `docs/OPTIONS.md`. - -Core user-facing apps (browsers, file managers, media players) are **not** installed by the `features/default.nix` module list. Instead, they are managed in the downstream `home.nix` (which the installer pre-fills with defaults like `firefox` and `thunar`). This keeps the Nomarchy core modules focused on the desktop environment while giving the user explicit control over their app set. - ---- - -## 3. Guardrails (non-negotiable unless the user overrides) - -These are inherited from the established Nomarchy conventions. Violating them is a bug. - -1. **Declarative-first.** No imperative state in `core/`. Mutable state goes in `~/.config/nomarchy/state.json` or in NixOS / home-manager options. -2. **Downstream-flake friendly.** Every behavior toggle is a `nomarchy.*` option documented in `docs/OPTIONS.md`. Adding a feature without a corresponding option is a bug. -3. **Opt-in by default.** New features default off (or default to existing behavior). The installer can flip defaults *for the user being installed*, but the option must read sensibly when set by hand. -4. **`lib.mkDefault` on *scalars* the user might override — never on lists/attrsets that merge.** If a downstream `system.nix` would reasonably want to change a scalar Nomarchy sets (a string, bool, int), set it with `lib.mkDefault`. If it must not be overridden, use `lib.mkForce` and explain why. **But do not wrap a list or attrset option (`home.packages`, `environment.systemPackages`, `boot.kernelParams`, …) in `lib.mkDefault`** when other modules also contribute to it: the module system's `filterOverrides` keeps only the highest-priority definitions, so a `mkDefault` list is *silently discarded the moment any module sets that option at normal priority* — it doesn't merge, it vanishes. This dropped the entire curated `home.packages` (firefox, mako, hyprlock, …) and broke notifications + the lock screen distro-wide (`1117dcf`, `f34f59c`). A plain list/attrset is the correct default — it merges, and downstream can still add to it (to *remove* a default, downstream uses `mkForce`/filtering, which is rare). -5. **Reuse before invent.** ~155 `nomarchy-*` scripts already exist across `core/system/scripts/`, `features/scripts/utils/`, `themes/engine/scripts/`. Grep before writing a new one. -6. **No comments that narrate.** Don't write comments explaining *what* the code does. Only write a comment when the *why* is non-obvious — a hidden constraint, a subtle invariant, a workaround. -7. **No backwards-compat shims.** If you remove a thing, remove it everywhere. No re-exports, no `// removed` markers. -8. **Docs ride with the change.** Every change ships in the same commit as its doc updates — no follow-up "docs catch-up" PRs. The mapping is in §5.4 below; if a change touches something in that table and the matching doc didn't move with it, the change is incomplete. - ---- - -## 4. How to find work to do - -The roadmap (`docs/ROADMAP.md`) is the source of truth. It has three columns and seven pillars. - -**Default rule:** prefer items in the **Now** column. Pick whichever matches the user's current ask, or the smallest one if no ask is in flight. - -If the user asked for something not in the roadmap: - -- Do the work. -- After it ships, propose a one-line addition to `docs/ROADMAP.md` so future you doesn't re-discover it. - -If the user asks "what's next?", reply with the Now column items in 2–3 sentences and let them pick. - -### The script & menu audit (Pillar 3) - -This is the **largest single open work item**. Phase A (inventory) hasn't run yet; `docs/SCRIPTS.md` is just scaffolding. The user explicitly flagged this pillar as important. - -Phase A shipped. The inventory lives at [`docs/SCRIPTS.md`](SCRIPTS.md) and is regenerated by: - -```bash -./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md -``` - -It tags every row with one of `kept` / `unused?` / `missing`. Phase B is the per-batch porting/removal: pick ~10 rows tagged `missing` or `unused?`, decide one of `port-from-omarchy` / `delete-dead` / `stub-with-notify`, and ship as one PR per batch on a `wave/audit-<batch>` branch. Each PR description should reference the rows it closes; reviewers spot-check that every caller of a removed/renamed script is updated. - ---- - -## 5. Workflow per change - -Steps you should follow for any non-trivial change: - -1. **Understand the current state first.** - - `git status` and `git log -5` so you know what's just landed and what's in flight. - - Read the relevant files. Don't infer. -2. **Plan if it's non-trivial.** Tasks with three or more steps benefit from a TaskCreate list. Tasks that touch the architecture benefit from plan mode. -3. **Reuse existing options and scripts.** Grep `core/system/options.nix`, `core/home/options.nix`, and the three script directories before adding anything. -4. **Touch the docs in the same change.** Use this table — if your change matches a row, the doc edit ships in the same commit. No exceptions, no follow-ups. - - | If you change… | Update in the same commit | - | --- | --- | - | A `nomarchy.*` option (added, removed, renamed, default flipped, type changed, description changed) | `docs/OPTIONS.md` (matching row) **and** any other row that mentions it (e.g. `formFactor` is referenced from `laptop.enable`'s row) | - | A `nomarchy-*` script (added, removed, renamed) | `docs/SCRIPTS.md` — the pre-commit hook regenerates it; verify it's staged. If `core.hooksPath` isn't set, run `./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md` manually. | - | A keybinding (`bindd =` / `bindeld =`) under `core/home/config/.../hypr/` | `docs/KEYBINDINGS.md` — regenerate via `./bin/utils/nomarchy-docs-keybindings --out docs/KEYBINDINGS.md`; spot-check the README highlights still hold | - | A directory was added/removed/renamed under `core/`, `features/`, `themes/`, `hosts/`, `installer/`, `lib/`, `bin/`, or `docs/` | `docs/STRUCTURE.md` (tree + module logic prose) **and** §2 of this file (`docs/AGENT.md`) if the change is structural enough to affect new-agent onboarding | - | A feature module's behavior changed in a way an existing user would notice (a default flipped, a service started/stopped, a key remapped) | `README.md` if it's user-visible at the marketing level; otherwise just `docs/OPTIONS.md` | - | An installer prompt added/removed/reordered, or generated-file shape changed | `docs/MIGRATION.md` if it affects hand-written `flake.nix` setups; `installer/install.sh` heredoc comments if a generated file's shape changed | - | A theme palette added/removed, or the theming engine's contract changed | `docs/creating-themes.md` and the palette count in `README.md` | - | A roadmap item shipped | Move the entry to the **Shipped** section at the bottom of `docs/ROADMAP.md`. Don't delete — that's our changelog. | - | A roadmap item discovered (new scope) | Add a one-line row to **Now**, **Next**, or **Later** in `docs/ROADMAP.md`. Don't tell the user verbally and forget. | - | A workflow / convention / guardrail you'd want a future agent to follow | `docs/AGENT.md` (this file). | - | A new flake input, output, or major architectural decision | `docs/STRUCTURE.md` and `docs/AGENT.md` §1. | - - Before declaring done, do one explicit pass: did the change cross any row above? If yes, is the matching doc edit staged? If you can't answer "yes" to both, you're not done. -5. **Verify the change evaluates** (cheap, do this before declaring done): - ```bash - nix --extra-experimental-features 'nix-command flakes' flake check --no-build - bash -n installer/install.sh # if you touched it - ``` - `flake check` only evaluates the four *default* configs — it never flips a `nomarchy.*` toggle. If you touched anything that only fires when a toggle is enabled (impermanence, a preset, an opt-in feature, the per-palette system theme), also run the toggle matrix, which layers each opt-in scenario onto the default config and forces it: - ```bash - ./bin/utils/nomarchy-eval-matrix - ``` - When you **add or rename an opt-in `nomarchy.*` option**, fold it into the relevant combined scenario in the `scenarios` attrset in that script (the scenarios bundle many compatible toggles per eval to stay fast on the CI runner) — otherwise the new surface is unverified and the next eval-time bug in it ships silently (this is exactly how the impermanence assertion and the vscode option rename reached `main`). The matrix also runs in CI (`.gitea/workflows/check.yml`). - First clone? Enable the repo's pre-commit hook so `docs/SCRIPTS.md` regenerates whenever you add/modify/remove a `nomarchy-*` script: - ```bash - git config core.hooksPath .githooks - ``` - For installer changes, also do a dry-run end-to-end: - ```bash - sudo /etc/install.sh --dry-run # in the live ISO or VM - ``` - For waybar / Hyprland visual changes, boot it and look. On a host with KVM you can do this headless: `nix build .#nixosConfigurations.default.config.system.build.vm`, run its `bin/run-*-vm` with `QEMU_OPTS="-display none -monitor unix:/tmp/mon.sock,server,nowait -vga none"`, then `echo "screendump /tmp/s.ppm" | socat - unix-connect:/tmp/mon.sock`, convert PPM→PNG, and inspect it (this is how the first Pillar 9 pass caught the boot-time Hyprland config-error overlay). The `nomarchy-test-live-iso` / `installerVm` flows are the ISO-side equivalents. If you genuinely can't boot anything, **say so** rather than claiming a visual change works. See ROADMAP §9 (Live VM runtime QA) for the full method. -6. **Commit narrowly.** One concept per commit. The commit subject is `<type>: <imperative summary>` (`feat:`, `fix:`, `docs:`, `chore:`). The body explains the why. -7. **Push only when the user asks.** Local commits are free; pushing publishes. - ---- - -## 6. Patterns worth knowing - -### Adding a new option - -```nix -# core/system/options.nix (or core/home/options.nix for home-side) -mything.enable = lib.mkEnableOption '' - Concise description that reads well in `nix eval`. Mention any - pre-conditions (groups, kernel modules, hardware needed). -''; -``` - -Then wire it inside a `config = lib.mkIf cfg.mything.enable { ... }` block and document it in `docs/OPTIONS.md`. - -### Filtering modules conditionally - -We do this for waybar (drop `battery` on desktop) in `features/desktop/waybar/default.nix:25-39`. Pattern: - -```nix -let - rawSettings = builtins.fromJSON (builtins.readFile configFile); - laptopOnly = [ "battery" "custom/battery" ]; - filterModules = mods: - if config.nomarchy.formFactor == "laptop" then mods - else builtins.filter (m: !(builtins.elem m laptopOnly)) mods; - settings = rawSettings // { - modules-right = filterModules (rawSettings.modules-right or []); - modules-center = filterModules (rawSettings.modules-center or []); - modules-left = filterModules (rawSettings.modules-left or []); - }; -in { ... } -``` - -### Form-factor (laptop vs desktop) - -`nomarchy.system.formFactor` and `nomarchy.formFactor` are the two halves of the same flag (system + home). Default `"laptop"`. The installer auto-detects via `compgen -G "/sys/class/power_supply/BAT*"` and writes the explicit value into both generated files. Use this option to gate any laptop-only UI / service. - -### Hybrid State (`state.json` + `nomarchy-state.nix`) - -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 - -User-PATH scripts ship via `nomarchy-system-scripts` (`core/system/scripts-derivation.nix`) plus the per-category dependencies declared in `features/scripts/default.nix:categoryDeps`. When you add a script: - -1. Drop the file in `features/scripts/utils/` or `core/system/scripts/`. -2. `chmod +x` it (it'll be wrapped with the right deps automatically). -3. Reference the right `categoryDeps` group in `features/scripts/default.nix` if it needs new tools. -4. Test that `which <script>` resolves after a rebuild. - -### Heredocs in `installer/install.sh` - -The system-config generator uses unquoted heredocs (`<< EOF`) so `$VAR` expands. The home-config generator now also uses unquoted heredocs — any literal `$` or backtick in the body must be escaped (see `installer/install.sh:1043-1131`). If you forget and the install fails with a "unbound variable" or unexpected command output, that's why. - ---- - -## 7. Things to never do without explicit user OK - -- `git push --force`, `git reset --hard`, `git clean -f`, `rm -rf` outside obvious sandbox dirs. -- Touch `flake.lock` unless the user asked to update inputs. -- Bump `nixpkgs` major version (e.g. `25.11` → `26.05`) — that's a release decision. -- Add a new flake input. They're load-bearing across all three host configurations. -- Skip pre-commit hooks (`--no-verify`). -- Ship a feature without a `nomarchy.*` option to gate it. -- Ship a removal without updating callers (grep first). -- Mock the database / external services in tests — use the real thing. - ---- - -## 8. Where to put your own notes - -- **Long-lived docs** → `docs/`. -- **Working notes / per-session plans** → `~/.claude/plans/`. Don't commit them. -- **Memory** about the user, this project, or process preferences → your harness's memory directory at `/home/bernardo/.claude/projects/-home-bernardo-Projects-nomarchy/memory/`. Each entry gets a frontmatter file plus a one-line index entry in `MEMORY.md`. - ---- - -## 9. The shape of a good handoff - -Before you stop, leave the next agent (or future you) something to land on: - -- The roadmap is up to date — items you shipped are in **Shipped**. -- `git status` is clean (or has one obvious WIP commit on a `wave/...` branch). -- If you discovered new scope, you logged it as a roadmap row, not a verbal heads-up. -- Any plan file you wrote in `~/.claude/plans/` is named after the task, not the date. -- The user's last message has been answered concisely. - -That's it. The roadmap tells you what; this file tells you how. Go pick a Now item. diff --git a/docs/HOOKS.md b/docs/HOOKS.md deleted file mode 100644 index 42c8224..0000000 --- a/docs/HOOKS.md +++ /dev/null @@ -1,57 +0,0 @@ -# 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...] -``` diff --git a/docs/KEYBINDINGS.md b/docs/KEYBINDINGS.md deleted file mode 100644 index 3f5cd1b..0000000 --- a/docs/KEYBINDINGS.md +++ /dev/null @@ -1,217 +0,0 @@ -# Nomarchy Keybindings - -Auto-generated from the Hyprland binding files. **Do not edit by hand.** -Re-run the generator after changing any `bindings/*.conf`: - -```bash -./bin/utils/nomarchy-docs-keybindings --out docs/KEYBINDINGS.md -``` - -`SUPER` is the Meta / Win key. `code:NN` keys (X11 digit keycodes) are -shown as the digit they correspond to. Media keys (`XF86Audio*`, -`XF86MonBrightness*`, …) are prettified. - -## Utilities - -_Source: `core/home/config/nomarchy/default/hypr/bindings/utilities.conf`_ - -| Modifiers | Key | Action | -| --- | --- | --- | -| SUPER | SPACE | Launch apps | -| SUPER CTRL | E | Emoji picker | -| SUPER CTRL | C | Capture menu | -| SUPER CTRL | O | Toggle menu | -| SUPER ALT | SPACE | Toggle top bar | -| SUPER | ESCAPE | System menu | -| — | XF86PowerOff | Power menu | -| SUPER | K | Show key bindings | -| — | XF86Calculator | Calculator | -| SUPER SHIFT | SPACE | Nomarchy menu | -| SUPER CTRL | SPACE | Theme background menu | -| SUPER SHIFT CTRL | SPACE | Theme menu | -| SUPER | BACKSPACE | Toggle window transparency | -| SUPER SHIFT | BACKSPACE | Toggle window gaps | -| SUPER CTRL | BACKSPACE | Toggle single-window square aspect | -| SUPER | COMMA | Dismiss last notification | -| SUPER SHIFT | COMMA | Dismiss all notifications | -| SUPER CTRL | COMMA | Toggle silencing notifications | -| SUPER ALT | COMMA | Invoke last notification | -| SUPER SHIFT ALT | COMMA | Restore last notification | -| SUPER CTRL | I | Toggle locking on idle | -| SUPER CTRL | N | Toggle nightlight | -| CTRL | F1 | Apple Display brightness down | -| CTRL | F2 | Apple Display brightness up | -| SHIFT CTRL | F2 | Apple Display full brightness | -| — | PRINT | Screenshot | -| ALT | PRINT | Screenrecording | -| SUPER | PRINT | Color picker | -| SUPER CTRL | S | Share | -| SUPER CTRL ALT | T | Show time | -| SUPER CTRL ALT | B | Show battery remaining | -| SUPER CTRL | A | Audio controls | -| SUPER CTRL | B | Bluetooth controls | -| SUPER CTRL | W | Wifi controls | -| SUPER CTRL | T | Activity | -| SUPER CTRL | Z | Zoom in | -| SUPER CTRL ALT | Z | Reset zoom | -| SUPER CTRL | L | Lock system | - -## Tiling - -_Source: `core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf`_ - -| Modifiers | Key | Action | -| --- | --- | --- | -| SUPER | W | Close window | -| CTRL ALT | DELETE | Close all windows | -| SUPER | J | Toggle window split | -| SUPER | P | Pseudo window | -| SUPER | T | Toggle window floating/tiling | -| SUPER | F | Full screen | -| SUPER CTRL | F | Tiled full screen | -| SUPER ALT | F | Full width | -| SUPER | O | Pop window out (float & pin) | -| SUPER | L | Toggle workspace layout | -| SUPER | LEFT | Move window focus left | -| SUPER | RIGHT | Move window focus right | -| SUPER | UP | Move window focus up | -| SUPER | DOWN | Move window focus down | -| SUPER | 1 | Switch to workspace 1 | -| SUPER | 2 | Switch to workspace 2 | -| SUPER | 3 | Switch to workspace 3 | -| SUPER | 4 | Switch to workspace 4 | -| SUPER | 5 | Switch to workspace 5 | -| SUPER | 6 | Switch to workspace 6 | -| SUPER | 7 | Switch to workspace 7 | -| SUPER | 8 | Switch to workspace 8 | -| SUPER | 9 | Switch to workspace 9 | -| SUPER | 0 | Switch to workspace 10 | -| SUPER SHIFT | 1 | Move window to workspace 1 | -| SUPER SHIFT | 2 | Move window to workspace 2 | -| SUPER SHIFT | 3 | Move window to workspace 3 | -| SUPER SHIFT | 4 | Move window to workspace 4 | -| SUPER SHIFT | 5 | Move window to workspace 5 | -| SUPER SHIFT | 6 | Move window to workspace 6 | -| SUPER SHIFT | 7 | Move window to workspace 7 | -| SUPER SHIFT | 8 | Move window to workspace 8 | -| SUPER SHIFT | 9 | Move window to workspace 9 | -| SUPER SHIFT | 0 | Move window to workspace 10 | -| SUPER SHIFT ALT | 1 | Move window silently to workspace 1 | -| SUPER SHIFT ALT | 2 | Move window silently to workspace 2 | -| SUPER SHIFT ALT | 3 | Move window silently to workspace 3 | -| SUPER SHIFT ALT | 4 | Move window silently to workspace 4 | -| SUPER SHIFT ALT | 5 | Move window silently to workspace 5 | -| SUPER SHIFT ALT | 6 | Move window silently to workspace 6 | -| SUPER SHIFT ALT | 7 | Move window silently to workspace 7 | -| SUPER SHIFT ALT | 8 | Move window silently to workspace 8 | -| SUPER SHIFT ALT | 9 | Move window silently to workspace 9 | -| SUPER SHIFT ALT | 0 | Move window silently to workspace 10 | -| SUPER | S | Toggle scratchpad | -| SUPER ALT | S | Move window to scratchpad | -| SUPER | TAB | Next workspace | -| SUPER SHIFT | TAB | Previous workspace | -| SUPER CTRL | TAB | Former workspace | -| SUPER SHIFT ALT | LEFT | Move workspace to left monitor | -| SUPER SHIFT ALT | RIGHT | Move workspace to right monitor | -| SUPER SHIFT ALT | UP | Move workspace to up monitor | -| SUPER SHIFT ALT | DOWN | Move workspace to down monitor | -| SUPER SHIFT | LEFT | Swap window to the left | -| SUPER SHIFT | RIGHT | Swap window to the right | -| SUPER SHIFT | UP | Swap window up | -| SUPER SHIFT | DOWN | Swap window down | -| ALT | TAB | Cycle to next window | -| ALT SHIFT | TAB | Cycle to prev window | -| ALT | TAB | Reveal active window on top | -| ALT SHIFT | TAB | Reveal active window on top | -| SUPER | code:20 | Expand window left | -| SUPER | code:21 | Shrink window left | -| SUPER SHIFT | code:20 | Shrink window up | -| SUPER SHIFT | code:21 | Expand window down | -| SUPER | mouse_down | Scroll active workspace forward | -| SUPER | mouse_up | Scroll active workspace backward | -| SUPER | mouse:272 | Move window | -| SUPER | mouse:273 | Resize window | -| SUPER | G | Toggle window grouping | -| SUPER ALT | G | Move active window out of group | -| SUPER ALT | LEFT | Move window to group on left | -| SUPER ALT | RIGHT | Move window to group on right | -| SUPER ALT | UP | Move window to group on top | -| SUPER ALT | DOWN | Move window to group on bottom | -| SUPER ALT | TAB | Next window in group | -| SUPER ALT SHIFT | TAB | Previous window in group | -| SUPER CTRL | LEFT | Move grouped window focus left | -| SUPER CTRL | RIGHT | Move grouped window focus right | -| SUPER ALT | mouse_down | Next window in group | -| SUPER ALT | mouse_up | Previous window in group | -| SUPER ALT | 1 | Switch to group window 1 | -| SUPER ALT | 2 | Switch to group window 2 | -| SUPER ALT | 3 | Switch to group window 3 | -| SUPER ALT | 4 | Switch to group window 4 | -| SUPER ALT | 5 | Switch to group window 5 | -| SUPER | Slash | Cycle monitor scaling | - -## Clipboard - -_Source: `core/home/config/nomarchy/default/hypr/bindings/clipboard.conf`_ - -| Modifiers | Key | Action | -| --- | --- | --- | -| SUPER | C | Universal copy | -| SUPER | V | Universal paste | -| SUPER | X | Universal cut | -| SUPER CTRL | V | Clipboard manager | - -## Media keys - -_Source: `core/home/config/nomarchy/default/hypr/bindings/media.conf`_ - -| Modifiers | Key | Action | -| --- | --- | --- | -| — | Volume Up | Volume up | -| — | Volume Down | Volume down | -| — | Mute | Mute | -| — | Mic Mute | Mute microphone | -| — | Brightness Up | Brightness up | -| — | Brightness Down | Brightness down | -| — | Kbd Brightness Up | Keyboard brightness up | -| — | Kbd Brightness Down | Keyboard brightness down | -| — | Kbd Backlight | Keyboard backlight cycle | -| ALT | Volume Up | Volume up precise | -| ALT | Volume Down | Volume down precise | -| ALT | Brightness Up | Brightness up precise | -| ALT | Brightness Down | Brightness down precise | -| — | Next Track | Next track | -| — | XF86AudioPause | Pause | -| — | Play/Pause | Play | -| — | Previous Track | Previous track | -| SUPER | Mute | Switch audio output | - -## Apps & web shortcuts - -_Source: `features/desktop/hyprland/config/bindings.conf`_ - -| Modifiers | Key | Action | -| --- | --- | --- | -| SUPER | RETURN | Terminal | -| SUPER ALT | RETURN | Tmux | -| SUPER SHIFT | RETURN | Browser | -| SUPER SHIFT | F | File manager | -| SUPER ALT SHIFT | F | File manager (cwd) | -| SUPER SHIFT | B | Browser | -| SUPER SHIFT ALT | B | Browser (private) | -| SUPER SHIFT | M | Music | -| SUPER SHIFT | N | Editor | -| SUPER SHIFT | D | Docker | -| SUPER SHIFT | G | Signal | -| SUPER SHIFT | O | Obsidian | -| SUPER SHIFT | SLASH | Passwords | -| SUPER SHIFT | A | ChatGPT | -| SUPER SHIFT ALT | A | Grok | -| SUPER SHIFT | C | Calendar | -| SUPER SHIFT | E | Email | -| SUPER SHIFT | Y | YouTube | -| SUPER SHIFT ALT | G | WhatsApp | -| SUPER SHIFT CTRL | G | Google Messages | -| SUPER SHIFT | P | Google Photos | -| SUPER SHIFT | X | X | -| SUPER SHIFT ALT | X | X Post | diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md deleted file mode 100644 index 6986767..0000000 --- a/docs/MIGRATION.md +++ /dev/null @@ -1,300 +0,0 @@ -# Migrating an existing NixOS install to Nomarchy - -You already have NixOS running and you want the Nomarchy desktop without -reformatting. This is the in-place path. Your `/`, `/home`, hostname, and -user account are preserved; Nomarchy is layered on as a flake input. - -> Prefer a clean disk? Skip to the [fallback](#fallback-clean-install-via-the-live-iso). - ---- - -## Prerequisites - -- NixOS 25.11. Older releases are not supported by the current Nomarchy - modules. -- Flakes enabled. If you don't have them yet: - ```nix - # somewhere in your existing config - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - ``` - Apply with `sudo nixos-rebuild switch` once before proceeding. - ---- - -## 1. Move to a flake-based config - -If you already have `/etc/nixos/flake.nix`, skip to step 2. - -Back your existing config up: - -```bash -sudo cp /etc/nixos/configuration.nix /etc/nixos/configuration.nix.bak -``` - -Then create `/etc/nixos/flake.nix` with the structure below. (This is the -same shape the Nomarchy installer produces — see -`installer/install.sh:generate_flake_config` in the upstream repo.) - -```nix -{ - description = "My Nomarchy Configuration"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; - nomarchy.url = "git+https://git.bemagri.xyz/bernardo/Nomarchy.git"; - # …or pin: "git+https://git.bemagri.xyz/bernardo/Nomarchy.git?rev=<sha>" - nixos-hardware.url = "github:NixOS/nixos-hardware/master"; - home-manager = { - url = "github:nix-community/home-manager/release-25.11"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { self, nixpkgs, nomarchy, home-manager, nixos-hardware, ... }@inputs: - let - system = "x86_64-linux"; - pkgs = import nixpkgs { - inherit system; - overlays = [ nomarchy.overlays.default ]; - config.allowUnfree = true; - }; - in - { - nixosConfigurations.<hostname> = nixpkgs.lib.nixosSystem { - inherit pkgs; - specialArgs = { inputs = nomarchy.inputs // inputs; }; - modules = [ - ./hardware-configuration.nix - nomarchy.nixosModules.system - ./system.nix - ]; - }; - - homeConfigurations.<user> = home-manager.lib.homeManagerConfiguration { - inherit pkgs; - extraSpecialArgs = { inputs = nomarchy.inputs // inputs; }; - modules = [ - nomarchy.nixosModules.home - ./home.nix - { - home.username = "<user>"; - home.homeDirectory = "/home/<user>"; - home.stateVersion = "25.11"; - } - ]; - }; - }; -} -``` - -Replace `<hostname>` and `<user>` with your real values. Two-track Nomarchy: -the system block uses `nomarchy.nixosModules.system`; the standalone -home-manager block uses `nomarchy.nixosModules.home`. Both consume the same -`pkgs` so overlays stay in sync. - -## 2. Move your existing settings into `system.nix` and `home.nix` - -The Nomarchy installer also generates these files — copy that template, or -create minimal versions to start: - -`/etc/nixos/system.nix`: - -```nix -{ pkgs, ... }: -{ - networking.hostName = "<hostname>"; - time.timeZone = "<your-timezone>"; - - users.users."<user>" = { - isNormalUser = true; - extraGroups = [ "wheel" "video" "render" "audio" "networkmanager" ]; - }; - - # Compressed RAM swap — near-free memory headroom. - zramSwap.enable = true; - - system.stateVersion = "25.11"; -} -``` - -`/etc/nixos/home.nix`: - -```nix -{ pkgs, ... }: -{ - imports = [ - # Machine-managed state (theme, font, toggles). - # UI scripts update this file automatically. - ./nomarchy-state.nix - ]; - - home.packages = with pkgs; [ - firefox - xfce.thunar - imv - mpv - fastfetch - chromium - # …add anything you want; mako/hyprlock/swww/rofi/etc. ship with Nomarchy. - ]; - - # Enable Nomarchy's curated app configurations - nomarchy.apps = { - alacritty.enable = true; - btop.enable = true; - elephant.enable = true; - swayosd.enable = true; - walker.enable = true; - vscode.enable = true; - }; -} -``` - -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 -them — see the [conflicts](#conflicts-to-resolve-before-rebuild) section. - -## 3. (Optional) Pick up hardware-specific tuning - -Nomarchy ships a `nixos-hardware` matcher. From any shell on your existing -system: - -```bash -nix shell nixpkgs#git -c \ - bash -c 'tmp=$(mktemp -d); git clone --depth=1 https://git.bemagri.xyz/bernardo/Nomarchy.git "$tmp" >/dev/null 2>&1; \ - source "$tmp/installer/hardware-db.sh"; nomarchy_detect_hw' -``` - -Output is a list like: - -``` -MODULE common-cpu-amd -MODULE common-gpu-amd -MODULE common-pc-laptop -MODULE framework-13-amd-ai-300-series -OPT isFramework=true -``` - -Drop a `hardware-selection.nix` next to your flake: - -```nix -{ inputs, ... }: -{ - imports = [ - inputs.nixos-hardware.nixosModules.common-cpu-amd - inputs.nixos-hardware.nixosModules.common-gpu-amd - inputs.nixos-hardware.nixosModules.framework-13-amd-ai-300-series - ]; - nomarchy.hardware.isFramework = true; -} -``` - -…and add `./hardware-selection.nix` to the system module list in `flake.nix`. -Skip this entirely if you don't have a matching device. - -## 4. Rebuild - -```bash -sudo nixos-rebuild switch --flake /etc/nixos#<hostname> -home-manager switch --flake /etc/nixos#<user> --impure -``` - -Reboot or `systemctl restart display-manager`. SDDM comes up with the -Nomarchy theme; logging in as `<user>` lands you in Hyprland with the full -Nomarchy desktop. - -After this, the daily workflow is two commands: - -- **System changes** (services, kernel, hardware) → - `sudo nixos-rebuild switch --flake /etc/nixos#<hostname>` -- **Dotfiles, themes, user packages** → `nomarchy-env-update` (runs the - standalone `home-manager switch` for you, no system rebuild) - ---- - -## Conflicts to resolve before rebuild - -`nomarchy.nixosModules.system` enables a desktop stack. If your existing -`configuration.nix` already configures any of these, only one setting wins -and it's whichever has higher Nix priority. Fix these explicitly: - -| Area | Nomarchy default | What to do if you already have it | -| --- | --- | --- | -| Display manager | SDDM (Wayland) | Disable yours: `services.xserver.displayManager.gdm.enable = lib.mkForce false;` (or whatever you had) | -| Default session | `hyprland-uwsm` | Drop your `services.displayManager.defaultSession` | -| Hyprland | `programs.hyprland.enable = true; withUWSM = true;` | Drop your `programs.hyprland` setting | -| Audio | PipeWire (alsa+pulse+jack) | Remove `services.pulseaudio.enable = true;` — NixOS errors if both are on | -| Networking | NetworkManager | Drop `networking.wireless.enable = true;` (if set) | -| Graphics | `hardware.graphics.enable = true` (was `hardware.opengl`) | Probably already enabled — fine | -| User groups | needs `video render networkmanager` | Add to your `users.users.<user>.extraGroups` | -| `/etc/os-release` | `ID=nomarchy`, `NAME=Nomarchy` | A few third-party scripts grep `ID=nixos` — adjust them or rely on `ID_LIKE` (TBD) | -| autoLogin | `enable = false; user = "nomarchy";` (mkDefault) | Off by default — opt in with `services.displayManager.autoLogin = { enable = true; user = "<your user>"; };` if you want it | - -Impermanence is **off** unless you set `nomarchy.system.impermanence.enable = true`, -and it requires a BTRFS layout with a `root-blank` snapshot. Don't enable it -on an existing install — the live ISO is the right path for that. - -If your first rebuild errors out, the five most common failures and their fixes -live in [Troubleshooting](TROUBLESHOOTING.md). - ---- - -## Fallback: clean install via the live ISO - -```bash -# In a checkout of the Nomarchy repo: -nomarchy-test-live-iso # boots the ISO in QEMU to evaluate -# Or burn the produced .iso to a USB and boot it on real hardware. -``` - -The ISO autologins to a Hyprland live session that points you at: - -- `sudo /etc/install.sh` — install (BTRFS + LUKS + subvolumes per - `installer/disko-config.nix`, auto-detects hardware via `hardware-db.sh`, - runs `home-manager switch` inside `nixos-enter` so the first login is - fully themed). -- `sudo /etc/install.sh --dry-run` — generate the flake into a tmpdir and - parse-check it without touching the disk. -- `sudo /etc/install.sh --resume` — pick up an interrupted run. - -After install, the system at `/etc/nixos/` is the same shape this guide -produces by hand. - -**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) - -1. `cat /etc/os-release` → `NAME=Nomarchy`, `ID=nomarchy`. -2. SDDM theme is the Nomarchy purple/blue panel. -3. After login: waybar shows the Nomarchy logo on the left, theme switcher - under Style → Theme works. -4. `which btop fastfetch waybar walker nomarchy-env-update` → all on PATH. -5. `nomarchy-env-update` returns clean (proves standalone HM is wired and - `nomarchy.nixosModules.home` is in scope). - -If anything is wrong, your old config is intact at -`/etc/nixos/configuration.nix.bak` — `sudo nixos-rebuild switch -I nixos-config=/etc/nixos/configuration.nix.bak` -will roll back. diff --git a/docs/OPTIONS.md b/docs/OPTIONS.md deleted file mode 100644 index 0bbdda5..0000000 --- a/docs/OPTIONS.md +++ /dev/null @@ -1,387 +0,0 @@ -# Nomarchy Options Reference - -Every option Nomarchy exposes for downstream flakes. Paths under `nomarchy.system.*` are NixOS options (set in `system.nix`); paths under `nomarchy.*` (no `system` segment) are Home Manager options (set in `home.nix`). `nomarchy.hardware.*` is NixOS. - -The installer-generated configuration writes a few of these for you (timezone, formFactor, hardware vendor flags, keymap, locale). Anything not listed there is opt-in — set it yourself. - -To see the live default for any option: - -```bash -nix eval .#nixosConfigurations.<host>.config.nomarchy.system.<path> -nix eval .#homeConfigurations.<user>.config.nomarchy.<path> -``` - ---- - -## NixOS options (`system.nix`) - -### `nomarchy.system.dns` - -DNS provider. One of `"DHCP"` (default), `"Cloudflare"`, `"Google"`, `"Custom"`. With `"Custom"`, set `nomarchy.system.customDns` to a list of nameservers. Anything other than `"DHCP"` also enables `services.resolved` with DNSSEC and DNS-over-TLS. - -Defined in `core/system/options.nix`; wired in `core/system/network.nix`. - -### `nomarchy.system.customDns` - -`listOf str`, default `[]`. Nameservers used when `dns = "Custom"`. - -### `nomarchy.system.wifi.powersave` - -`bool`, default `true`. Sets `networking.networkmanager.wifi.powersave`. Turn off if you see drops on idle Wi-Fi. - -### `nomarchy.system.timezone` - -`str`, default `"UTC"`. The installer writes `time.timeZone` directly, so this option is informational unless you wire it into your own modules. - -### `nomarchy.system.formFactor` - -`enum [ "laptop" "desktop" ]`, default `"laptop"`. Drives UI affordances and the laptop power preset. The installer auto-detects via `/sys/class/power_supply/BAT*`. The default is `"laptop"` because the battery widget renders empty when no battery is present — safe on a desktop, useful on a laptop. - -Wired in `features/desktop/waybar/default.nix` (filters the battery widget out on desktop), `features/scripts/battery-monitor.nix` (skips the timer on desktop), and `nomarchy.system.laptop.enable` (defaults true when this is `"laptop"`). - -### `nomarchy.system.theme` - -`str`, default `"summer-night"`. Theme name. Mirror of the home-side `nomarchy.theme`. Set both if you also want NixOS-side modules (e.g. SDDM theming) to follow. - -### `nomarchy.system.features.fingerprint` - -`bool`, default `false`. Enables `services.fprintd.enable`. - -### `nomarchy.system.features.fido2` - -`bool`, default `false`. Enables `security.pam.u2f` (sufficient, with cue). - -### `nomarchy.system.features.hybridGPU` - -`bool`, default `false`. NVIDIA-hybrid laptop support. Wires: - -- `services.supergfxd.enable` for runtime mode switching (`Integrated` / `Hybrid` / `Vfio` / `AsusEgpu`), driven by `nomarchy-toggle-hybrid-gpu`. -- The NVIDIA driver stack (`services.xserver.videoDrivers = ["nvidia"]`, `hardware.graphics.{enable,enable32Bit}`, `hardware.nvidia.{modesetting,powerManagement}.enable`, `boot.kernelParams = ["nvidia-drm.modeset=1"]`). - -All driver knobs use `lib.mkDefault`, so a downstream `system.nix` can pin a beta driver or flip to the open kernel module without forking the module. - -**You still have to add bus IDs** — they're per-machine and can't be derived from any flag. Find them with `lspci -D | grep -E 'VGA|3D'`, then in your `/etc/nixos/system.nix`: - -```nix -hardware.nvidia.prime = { - offload.enable = true; - offload.enableOffloadCmd = true; - intelBusId = "PCI:0:2:0"; # or `amdgpuBusId` for AMD iGPU - nvidiaBusId = "PCI:1:0:0"; -}; -``` - -Without prime config, supergfxd still switches modes but render-offload via `nvidia-offload <cmd>` is unavailable. - -### `nomarchy.system.snapper.enable` - -`bool`, default `false`. BTRFS timeline snapshots of `/`. Auto-disables when `/` isn't BTRFS. Includes a `nixos-rebuild-snap` wrapper that takes a "Pre-rebuild" snapshot before each switch. - -### `nomarchy.system.hibernation.enable` - -`bool`, default `false`. Suspend-then-hibernate on lid close, idle, and power button. Requires a disk swap device or swapfile sized to at least RAM — zRAM alone is not enough. - -### `nomarchy.system.hibernation.idleMinutes` - -`int`, default `30`. Idle minutes before suspend-then-hibernate fires. - -### `nomarchy.system.laptop.enable` - -`bool`, default `nomarchy.system.formFactor == "laptop"`. Laptop power preset: TLP (with sane AC/battery governors and ThinkPad-style 75/80 charge thresholds), `services.upower`, `services.thermald` (gated by `laptop.thermald`), and a brightnessctl udev rule so the existing `nomarchy-brightness-{display,keyboard}` scripts run without root. Force-disables `services.power-profiles-daemon` (mutually exclusive with TLP) — to use PPD instead, set `laptop.enable = false` and wire it yourself. Lid-close action defers to `nomarchy.system.hibernation.enable`: `suspend-then-hibernate` when on, `suspend` otherwise. Charge thresholds are only honored on supported hardware (ThinkPad, some ASUS); harmless warning elsewhere. - -### `nomarchy.system.laptop.thermald` - -`bool`, default `true` on x86_64. Enables `services.thermald` (Intel thermal daemon). Harmless no-op on AMD; gated off on aarch64. - -### `nomarchy.system.desktop.enable` - -`bool`, default `nomarchy.system.formFactor == "desktop"`. Desktop preset: pins `powerManagement.cpuFreqGovernor` to `"performance"` (via `mkDefault`) and enables `services.zfs.autoScrub` + `services.zfs.trim` so a future ZFS pool gets sensible maintenance without further config. The ZFS knobs are no-ops until you add `boot.supportedFilesystems = [ "zfs" ]` (plus `networking.hostId`) and a pool. Battery-widget filtering is handled by `formFactor` itself, so this preset doesn't repeat it. - -### `nomarchy.system.accessibility.enable` - -`bool`, default `false`. Accessibility preset: enables `services.gnome.at-spi2-core` (AT-SPI2 framework), installs `pkgs.orca` (screen reader) into `environment.systemPackages`, and sets `XCURSOR_SIZE` to `accessibility.cursorSize`. Off by default — accessibility is a personal preference, not hardware-derived. The Hyprland-side bits (slower key-repeat, Orca launch keybinding, high-contrast palette) are a separate roadmap item. - -### `nomarchy.system.accessibility.cursorSize` - -`int`, default `32`. `XCURSOR_SIZE` when `accessibility.enable = true`. NixOS default is 24; 32 is a safer floor for low-vision users. - -### `nomarchy.system.gaming.enable` - -`bool`, default `false`. Gaming preset: enables `programs.steam` (with `remotePlay` and `localNetworkGameTransfers` firewall holes opened by `mkDefault`), `programs.gamemode` (the launching user must be in the `gamemode` group), and `services.flatpak`. The flathub remote isn't added declaratively — after first boot, run `flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo`. Pair with the home-side companion `nomarchy.gaming.enable` for the Hyprland fullscreen-on-Steam-launch window rule. - -### `nomarchy.system.containers.enable` - -`bool`, default `false`. Rootless Podman with Docker compatibility (`docker` → `podman`), plus `podman-compose`, `podman-tui`, and `dive`. - -### `nomarchy.system.virtualization.libvirt.enable` - -`bool`, default `false`. libvirt daemon, virt-manager, and OVMF. Add your user to the `libvirtd` group. - -### `nomarchy.system.virtualization.docker.enable` - -`bool`, default `false`. Docker daemon and docker-compose. Add your user to the `docker` group. - -### `nomarchy.system.keyring.enable` - -`bool`, default `true`. Auto-unlock GNOME Keyring at login and route SSH keys through `gcr-ssh-agent`. On by default — near-universal QoL improvement. - -### `nomarchy.system.inputMethod.enable` - -`bool`, default `false`. fcitx5 input method for CJK / IME. Wires NixOS's `i18n.inputMethod` and autostarts `fcitx5-daemon`. - -### `nomarchy.hardware.isXPS` - -`bool`, default `false`. Dell XPS fixes — haptic touchpad service and PCI/I²C power-control udev rules. - -### `nomarchy.hardware.isT2Mac` - -`bool`, default `false`. Apple T2 MacBook fixes — IOMMU kernel params, `apple-bce` module, brcmfmac feature mask. - -### `nomarchy.hardware.isFramework` - -`bool`, default `false`. Framework laptop QMK HID udev rule. - -### `nomarchy.hardware.fwupd` - -`bool`, default `false`. Enables `services.fwupd` firmware update service. - -### `nomarchy.hardware.hasIPU7Camera` - -`bool`, default `false`. Intel IPU7 camera support (kernel modules + firmware). - -### `nomarchy.system.impermanence.enable` - -`bool`, default `false`. Erase Your Darlings root wipe on boot. Defined in `core/system/impermanence.nix`. The installer writes the flag based on the impermanence prompt. - -### `nomarchy.system.impermanence.mainLuksName` - -`str`, default `"crypted"`. Name of the `/dev/mapper` entry holding the BTRFS root. The disko layout uses `"crypted"` on single-disk installs and `"crypted_main"` once multiple drives are selected — the installer writes the matching value automatically. - -### `nomarchy.system.impermanence.user` - -`str`, default `"nomarchy"`. Primary user whose home subset (`.ssh`, `.gnupg`, `.local/share/keyrings`, `Documents`, `Downloads`, `Pictures`, `Videos`, `Projects`) survives the rootfs wipe. Must match the user created via `users.users.<name>` — otherwise the persistence block is silently inert and the user's home directory is wiped on every boot. The installer writes this for you. - ---- - -## Home Manager options (`home.nix`) - -### `nomarchy.theme` - -`str`, default `"summer-night"`. Active theme name. Available themes are the directories under `themes/palettes/`. - -### `nomarchy.panelPosition` - -`enum ["top", "bottom"]`, default `"top"`. Waybar panel position. - -### `nomarchy.hyprland.gaps_in` / `nomarchy.hyprland.gaps_out` / `nomarchy.hyprland.border_size` - -`int`, defaults `5` / `10` / `2`. Hyprland window inner gaps, outer gaps, and active-border width (px). Map to Hyprland's `general.{gaps_in,gaps_out,border_size}` in `features/desktop/hyprland/default.nix`. Defaults come from `lib/state-schema.nix` and are read from `~/.config/nomarchy/state.json`, so they can be retuned without a rebuild; set them here to make a choice permanent. - -### `nomarchy.formFactor` - -`enum [ "laptop" "desktop" ]`, default `"laptop"`. Mirror of `nomarchy.system.formFactor`. Filters laptop-only widgets out of waybar (battery) when set to `"desktop"`. The installer writes both system and home values together. - -### `nomarchy.keymap.layout` / `nomarchy.keymap.variant` - -`str` / `str`, defaults `"us"` / `""`. Keyboard layout and variant for Hyprland's native Wayland session. `system.nix` writes `services.xserver.xkb.layout` (XWayland) and `console.keyMap` (TTY) to the same value, but Hyprland reads its own input config so this option must be set independently — otherwise a non-US user gets the right layout in XWayland apps and the console but the US fallback inside native-Wayland apps. The installer writes both fields into the generated `home.nix` alongside `nomarchy.formFactor`. Example: `nomarchy.keymap = { layout = "dk"; variant = ""; };`. - -### `nomarchy.wallpaper` - -`str`, default `""`. Absolute path to a wallpaper override. Empty string means "use the active theme's default wallpaper". - -### `nomarchy.toggles.suspend` - -`bool`, default `true`. Whether suspend appears in the system menu. - -### `nomarchy.toggles.screensaver` - -`bool`, default `true`. Whether the screensaver is enabled. - -### `nomarchy.toggles.idle` - -`bool`, default `true`. Whether the idle lock is enabled (hypridle). - -### `nomarchy.toggles.nightlight` - -`bool`, default `false`. Enables hyprsunset. - -### `nomarchy.toggles.waybar` - -`bool`, default `true`. Whether the top bar is deployed at all. - -### `nomarchy.nightlightTemperature` - -`int`, default `4000`. Nightlight color temperature (Kelvin). - -### `nomarchy.hyprland.gaps_in` - -`int`, default `5`. Inner gaps. - -### `nomarchy.hyprland.gaps_out` - -`int`, default `10`. Outer gaps. - -### `nomarchy.hyprland.border_size` - -`int`, default `2`. Window border width. - -### `nomarchy.fonts.monospace` - -`str`, default `"JetBrainsMono Nerd Font"`. Used by terminals, VSCode, etc. - -### `nomarchy.iconsTheme` - -`str`, default derived from the active theme (falls back to `"Yaru-blue"`). GTK/Qt icon theme name. `core/home/state.nix` computes this from the theme's palette metadata; override to pin a specific icon theme regardless of palette. - -### `nomarchy.isLightMode` - -`bool`, default derived from the active theme. Whether the active theme is a light theme. `core/home/state.nix` computes this from the theme directory; affects nightlight defaults and a few app theme decisions. Override only if you need to force a specific value. - -### `nomarchy.cursor.name` - -`str`, default `"Bibata-Modern-Ice"`. Cursor theme name. - -### `nomarchy.cursor.package` - -`package`, default `pkgs.bibata-cursors`. Package providing the cursor theme. Override both `name` and `package` together if you switch themes. - -### `nomarchy.configOverrides` - -`nullOr path`, default `null`. Path to a replacement config directory. When set, the items listed in `core/home/configs.nix` (`fastfetch`, `fcitx5`, `fontconfig`, `git`, `imv`, `nautilus-python`, `nomarchy`, `nomarchy-skill`, `uwsm`, `wiremix`, plus the loose files) are read from `<this-path>/<name>` instead of the bundled defaults. Distinct from `nomarchy.overrides.*` below — `configOverrides` is a working *bulk* redirect; `overrides.paths` is a *per-file* attrset map. - -### `nomarchy.apps.alacritty.enable` - -`bool`, default `false`. Enables Nomarchy's curated Alacritty configuration. The installer enables this by default. - -### `nomarchy.apps.btop.enable` - -`bool`, default `false`. Enables Nomarchy's curated btop configuration (themed color scheme, vim keys). The installer enables this by default. - -### `nomarchy.apps.ghostty.enable` - -`bool`, default `false`. Deploys Nomarchy's curated Ghostty configuration. - -### `nomarchy.apps.kitty.enable` - -`bool`, default `false`. Deploys Nomarchy's curated Kitty configuration. - -### `nomarchy.apps.lazygit.enable` - -`bool`, default `false`. Deploys Nomarchy's curated lazygit configuration. - -### `nomarchy.apps.tmux.enable` - -`bool`, default `false`. Deploys Nomarchy's curated tmux configuration. - -### `nomarchy.apps.elephant.enable` - -`bool`, default `false`. Enables Nomarchy's curated Elephant menu provider integration (required for the theme picker and background selector). The installer enables this by default. - -### `nomarchy.apps.walker.enable` - -`bool`, default `false`. Enables Nomarchy's curated Walker launcher configuration. The installer enables this by default. - -### `nomarchy.apps.swayosd.enable` - -`bool`, default `false`. Enables Nomarchy's curated SwayOSD configuration. The installer enables this by default. - -### `nomarchy.apps.vscode.enable` - -`bool`, default `false`. Enables Nomarchy's curated VSCode configuration (theming, fonts). The installer enables this by default. - -### `nomarchy.apps.vscode.devExtensions` - -`bool`, default `false`. Install Nomarchy's curated VSCode extension pack (language servers + git + editor enhancements). The palette theme extensions are *always* installed when `vscode.enable` is true — every palette except `ethereal`, `hackerman`, and `vantablack` resolves its `workbench.colorTheme` via either `pkgs.vscode-extensions` (catppuccin/nord/tokyo-night/rose-pine/gruvbox) or `pkgs.vscode-utils.extensionFromVscodeMarketplace` with version + sha256 pinned (sainnhe.everforest, qufiwefefwoyn.kanagawa, monokai.theme-monokai-pro-vscode, oldjobobo.{lumon,miasma,retro-82}, shadesOfBuntu.flexoki-light, jovejonovski.ocean-green, TahaYVR.matteblack, Bjarne.white-theme). The three palettes whose theme extension isn't on the marketplace fall back to VSCode's default theme; see the ROADMAP Later row. - -### `nomarchy.apps.opencode.enable` - -`bool`, default `false`. opencode AI coding CLI integration. Deploys `~/.config/opencode/opencode.json`. The `opencode` package itself is **not** installed by Nomarchy — add it to your `home.packages`. - -### `nomarchy.gaming.enable` - -`bool`, default `false`. Home-side companion to `nomarchy.system.gaming.enable`. Adds a Hyprland `windowrulev2 = fullscreen, class:^(steam_app_).*$` so games launched through Steam grab the whole screen instead of opening windowed. Set to the same value as the system option; the installer flips both when the Gaming profile is selected. - -### `nomarchy.accessibility.enable` - -`bool`, default `false`. Home-side companion to `nomarchy.system.accessibility.enable`. Three Hyprland-side adjustments: slows `input.repeat_rate` to `25` (default `40`) and `input.repeat_delay` to `1000` ms (from `600`) so holding a key isn't a runaway machine-gun for low-mobility users; binds `SUPER+ALT+S` to launch the Orca screen reader (the system preset already puts `orca` on PATH). Set to the same value as the system option. Update the bullet in `docs/ROADMAP.md` if a high-contrast palette ships as a `themes/palettes/` entry — it'll be gated on this option. - -### `nomarchy.themeLoader.enable` - -`bool`, default `true`. Auto-load theme-specific app configs from the active theme's `apps/` directory. Disable if you want to provide your own. - -### `nomarchy.themeLoader.apps.btop` - -`bool`, default `true`. Deploy the active theme's `apps/btop.theme` to `~/.config/btop/themes/nomarchy.theme`. The only per-app toggle in this group — waybar themes inline from `colorScheme` in `features/desktop/waybar`; kitty and alacritty are themed by stylix targets (`themes/engine/stylix.nix`); mako has no theme integration yet. - -### `nomarchy.overrides.enable` - -`bool`, default `true`. Whether the entries in `nomarchy.overrides.paths` are applied. Default `true` so an empty `paths` is a no-op and a populated one Just Works without a second toggle. - -### `nomarchy.overrides.paths` - -`attrsOf path`, default `{}`. Per-file overrides. Each key is an `xdg.configFile` path (relative to `~/.config/`); each value is a Nix path whose contents replace the Nomarchy-shipped source. Substitution is done with `lib.mkForce` so it wins over Nomarchy's own `lib.mkDefault` writes. Other fields on the original entry (`recursive` etc.) survive the merge. Overriding a path Nomarchy doesn't manage just creates a new `xdg.configFile` entry. - -Example: - -```nix -nomarchy.overrides.paths = { - "nomarchy/default/hypr/looknfeel.conf" = ./looknfeel.conf; - "waybar/style.css" = ./waybar.css; -}; -``` - -Drop-in-a-dir discovery (a runtime walk of `~/.config/nomarchy/overrides/`) is intentionally not supported — Nix needs every managed file declared at evaluation time, and the attrset is the explicit-config shape the rest of the Nomarchy module surface uses. - ---- - -## Examples - -### Minimal `system.nix` for a desktop with Cloudflare DNS, Snapper, and rootless Podman - -```nix -{ - nomarchy.system = { - dns = "Cloudflare"; - formFactor = "desktop"; - snapper.enable = true; - containers.enable = true; - }; -} -``` - -### Minimal `home.nix` for a Tokyo-night user with custom gaps and opencode - -```nix -{ - nomarchy = { - theme = "tokyo-night"; - hyprland.gaps_in = 8; - hyprland.gaps_out = 16; - apps.opencode.enable = true; - }; -} -``` - -### Ship your own Hyprland keybindings instead of Nomarchy's defaults - -Nomarchy deploys its `bindings.conf` with `lib.mkDefault`, so a higher-priority assignment from your own `home.nix` wins: - -```nix -{ - xdg.configFile."hypr/bindings.conf".source = ./my-bindings.conf; -} -``` - -The same pattern works for any file Nomarchy deploys via `xdg.configFile.<path>.source = lib.mkDefault …` — point at your own file and skip the default. - ---- - -## Where these are defined - -- `core/system/options.nix` — most `nomarchy.system.*` options -- `core/system/hardware.nix` — `nomarchy.hardware.*` -- `core/system/impermanence.nix` — `impermanence.enable` -- `core/home/options.nix` — most home-side `nomarchy.*` options -- `core/home/overrides.nix` — `nomarchy.overrides.*` -- `themes/engine/loader.nix` — `nomarchy.themeLoader.*` -- `features/apps/vscode.nix` — `nomarchy.vscode.*` diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md deleted file mode 100644 index 6a44e3e..0000000 --- a/docs/ROADMAP.md +++ /dev/null @@ -1,279 +0,0 @@ -# Nomarchy Roadmap - -This is the mid-term plan for Nomarchy. It exists so future sessions — human or agent — can pick up work without re-deriving context. Items move from **Now** → **Next** → **Later** as priorities shift, and from any column into **Shipped** at the bottom when done. There are no dates: ship-when-ready. - -If you're new here, also read [`docs/STRUCTURE.md`](STRUCTURE.md) and [`docs/OPTIONS.md`](OPTIONS.md). Existing-NixOS users should also read [`docs/MIGRATION.md`](MIGRATION.md). - -## 1. Vision & guardrails - -Nomarchy is a NixOS-based distribution that gives you a polished Hyprland desktop (Hyprland + waybar + walker + a curated theming engine) on a strictly declarative, flake-based foundation. Goal: power-user polish without giving up reproducibility. - -Guardrails (apply when adding anything): - -- **Declarative-first.** No imperative state in `core/`. Anything mutable lives in `~/.config/nomarchy/state.json` or in NixOS options. -- **Downstream-flake friendly.** Every behavior toggle is a `nomarchy.*` option documented in `docs/OPTIONS.md`. Adding a feature without a corresponding option is a bug. -- **Opt-in by default.** New features default off (or default to the existing behavior). The installer can flip defaults for the user, but the option must read sensibly when set by hand. -- **Reuse before invent.** Before adding a script, grep `core/system/scripts/`, `features/scripts/utils/`, and `themes/engine/scripts/` — there are ~155 of them, and many of the things you want already exist. - -## 2. Now / Next / Later board - -### Now (ready to pick up) - -_(empty — all entries shipped or moved to Later)_ - -### Next (bigger lifts that build on Now) - -### Later (speculative or research-shaped) - -- **Rolling vs pinned channel choice in the installer.** - Today the generated flake pins `nomarchy` to a rev. Offer a "rolling" option that follows `main` and a `nomarchy-rollback` helper for stuck rebuilds. -- **Theme creation wizard.** A `nomarchy-theme-new` script that scaffolds a new palette from a base16 hex set (or by sampling a wallpaper), runs `nomarchy-themes-prebuild`, and opens a PR template. -- **CI matrix on Forgejo Actions.** On every push: `nix flake check`, build `nomarchy-installer`, `nomarchy-live`, `default`. On tag: publish ISOs as release artefacts. -- **Golden-image VM tests per palette.** A `nixosTest` per palette that boots the `default` config, takes a screenshot, and diffs against a golden image. Catches Stylix regressions before they hit users. -- **Forgejo release pipeline.** `vYY.MM.x` tags matching the upstream NixOS channel; the pipeline pushes the three ISOs and an updated `flake.lock` snapshot. -- **Optional `nomarchy-installer-vm`** rebuilt as a real flake app (not a one-off shell script) so users can install Nomarchy into a libvirt VM declaratively. -- **Surface support module** via the relevant `nixos-hardware` profile + Surface kernel patches behind a `nomarchy.hardware.isSurface` toggle. -- **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. -- **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 - -Nomarchy ships **~155** `nomarchy-*` scripts across three directories, plus a 379-line `nomarchy-menu` with 23 submenu functions. Some are first-class Nomarchy work; some are direct Omarchy ports that haven't been adapted; some are dangling references the menu calls but no script implements (e.g. `nomarchy-backup`, `nomarchy-debug`, `nomarchy-pkg`, `nomarchy-pkg-aur-add`, `nomarchy-plymouth`, `nomarchy-refresh-hyprland`, `nomarchy-reinstall`, `nomarchy-rollback`, `nomarchy-screenrecord-filename`, `nomarchy-theme`, `nomarchy-update-firmware`, `nomarchy-upload-log`, `nomarchy-version`, `nomarchy-wallpaper`, `nomarchy-skill`, `nomarchy-luks`). - -This pillar fixes that. It runs as two phases. - -### Phase A — Inventory & triage - -Lands as a single PR. Output is `docs/SCRIPTS.md` populated with rows for every script and every menu item. - -1. Run a generator (one-shot helper, doesn't have to be checked in) that produces three lists: - - All `nomarchy-*` scripts under `core/system/scripts/`, `features/scripts/utils/`, `themes/engine/scripts/`. - - All `nomarchy-*` *callers* (grep `core/`, `features/`, `themes/`, `installer/`, `bin/`). - - The set difference (orphaned callers ↔ unreferenced scripts). -2. Walk `features/scripts/utils/nomarchy-menu` and list every menu entry with its target script. -3. Tag each row with a status: - - `kept` — works on Nomarchy, no change needed. - - `port-from-omarchy` — exists upstream, needs adapting (drop pacman/yay/AUR, repath to NixOS, talk to `nomarchy.system.*` options). - - `delete-dead` — neither used nor needed; remove and update callers. - - `stub-with-notify` — temporarily replace with a `notify-send "Not yet implemented in Nomarchy"` so the menu stops looking broken until the work is scheduled. - - `unknown` — needs a deeper look before tagging. -4. The completed table lives at [`docs/SCRIPTS.md`](SCRIPTS.md). The roadmap links to it; this section just sets the methodology. - -### Phase B — Adapt or remove - -Lands as PR batches of ~10 scripts each, branch named `wave/audit-<batch>`. Per script: - -- For `port-from-omarchy`: rewrite the script for Nomarchy paths (`/etc/nixos`, `nixos-rebuild`, `home-manager`, no Arch idioms), wire it into `nomarchy.system.*` where applicable, and update every caller (menu, waybar, keybindings). -- For `delete-dead`: `git rm` the script *and* fix every caller — a `find` + `sed` pass against `nomarchy-menu`, every `*.conf`, and every nix file. -- For `stub-with-notify`: write the one-liner stub in place. The roadmap row stays open until the real implementation lands. - -Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes, and reviewers spot-check that no caller still points at a stale name. - -## 4. Pillar: Installer - -- "What's installed?" summary screen on boot of a freshly-installed system, sourced from `state.json` + `nomarchy-system-scripts` introspection (Shipped). -- Richer disk metadata (Shipped). -- `disko-golden.nix` variants for software-RAID and BTRFS-pool-as-root (Shipped). -- Pre-flight resume polish (Shipped). -- Software-profile multi-select (Shipped). -- Form-factor → laptop preset (Shipped). - -## 5. Pillar: Power, hardware, presets - -- Auto-detect dGPU presence in `installer/hardware-db.sh` and pre-fill `hardware.nvidia.prime.{intel,nvidia}BusId` in the generated `system.nix` (driver stack itself is Shipped — see entry below). -- Surface support behind `nomarchy.hardware.isSurface` (Later). -- Laptop preset: TLP, upower, brightness, lid, hypridle tuning (Shipped). -- Desktop preset: performance governor, no laptop UI (already filtered), ZFS hooks (Shipped). -- Accessibility preset (Shipped). -- Gaming preset (Shipped). -- Vendor matchers in `installer/hardware-db.sh` (Shipped — ROG Ally added; Surface/Framework/Lenovo entries corrected; Steam Deck + Snapdragon X documented as nixos-hardware-unsupported. CI now lints DB references). - -## 6. Pillar: Onboarding & docs - -- `nomarchy-welcome` first-run wizard (Shipped). -- `docs/KEYBINDINGS.md` auto-generator (Shipped). -- `docs/TROUBLESHOOTING.md` (Shipped). -- `docs/index.md` / README docs index (Shipped — `README.md` links every doc in `docs/`). -- `nomarchy-manual` — opens the local `~/.local/share/nomarchy/README.md` via `xdg-open` (Shipped). - -## 7. Pillar: Test, CI, release - -- Forgejo Actions workflow: - - on every push to `main`: `nix flake check` (≈ what we run by hand today). - - on every PR: also build all three ISOs (cache hit on most of them). - - on tag `vYY.MM.x`: publish ISOs as release artefacts. -- Versioning scheme: `vYY.MM.x` matching the upstream NixOS channel (e.g. `v25.11.3`). -- `nixosTest` per palette: boots `default` in a VM, screenshots the SDDM splash and the Hyprland desktop, diffs vs golden. Failure surfaces as CI red. -- A small `bin/utils/nomarchy-bench-iso-build` that records ISO build time + size into a per-commit JSON so we notice regressions. - -## 8. Pillar: QA audit — features & components - -Nomarchy now spans an installer, ~159 `nomarchy-*` scripts, a Hyprland desktop stack (Hyprland + waybar + walker + nightlight + idle), curated apps, a 22-palette theme engine, and two ISO hosts. Pillar 3 audited script *existence*; this pillar audits feature *behavior*. The goal: walk every shipped feature end-to-end on a real install, fix every bug or surprise inline when small, and capture the rest as new roadmap rows. - -Runs as **per-component sweeps**. One PR per component, branch `wave/qa-<component>`. Don't grow scope mid-PR — bugs that need a new option, refactor, or missing module become a new **Now**/**Next** row. - -Components (each is one sweep): - -1. **Installer** — `installer/install.sh`, `installer/hardware-db.sh`, disko configs. Fresh install + `--resume` + `--dry-run`, on laptop and desktop, with FDE (non-LUKS branch is Later). Verify every generated file (`flake.nix`, `system.nix`, `home.nix`, `hardware-selection.nix`, `state.json`) is correct and idempotent. -2. **First-boot UX** — `nomarchy-welcome`, generated `home.nix`, SDDM and Plymouth metadata, default theme/font/panel position. Re-run on a clean VM; note every prompt that confuses and every default that's wrong. -3. **Core system modules** — `core/system/*` (laptop, desktop, accessibility, gaming, hybridGPU, impermanence, network, hardware, branding). For each: enable → rebuild → observe the claimed effect → disable → rebuild → observe it's gone. Cross-check against `docs/OPTIONS.md`. -4. **Core home modules** — `core/home/*` (options, state, behavior, overrides, deployed config). Verify every home-side `nomarchy.*` option does what its description claims; confirm `~/.config/nomarchy/overrides/` actually overrides. -5. **Desktop stack** — Hyprland (keybindings, window rules, monitors, input), waybar (every module × both panel positions × both form factors), walker (every launcher mode), idle, nightlight, notifications (mako). Reconcile `docs/KEYBINDINGS.md` against runtime. -6. **Apps** — `features/apps/*`. Each app: launches, themed via Stylix, configured as expected. Catches the "we package it but nobody configured it" class. -7. **Theme engine + palettes** — `nomarchy-theme-set` across all 22 palettes, font and wallpaper switchers, light-mode toggle. Verify per-palette Stylix targets render correctly across SDDM, Plymouth, GTK, Qt, terminals, browsers, waybar, walker. -8. **Scripts (runtime behavior)** — Pillar 3 confirmed existence; this sweep runs every user-visible script (especially every `nomarchy-menu` entry) on current NixOS and confirms it actually does the thing. -9. **ISOs** — boot `nomarchy-installer` and `nomarchy-live`; verify the `nomarchy-test-live-iso` flow; check the installer ISO ships every tool `install.sh` calls (regression class: `hardware-db.sh` missing, already shipped). -10. **Lib + state schema** — `lib/state-schema.nix`, color resolution, path helpers. Cross every codepath that produces `state.json` (installer, welcome wizard, hand-edit) against the schema; confirm bad inputs are rejected with a useful message. - -Per-PR deliverable: - -- PR body lists what was tested, what was broken, what was fixed inline, what was deferred (with the new roadmap row linked). -- Doc updates ride with the change per `docs/AGENT.md` §5.4. -- Don't bundle fixes across components — keep one component per branch so reviewers can spot-check end-to-end without context-switching. - -Pillar is **done** when every component has a closed `wave/qa-<component>` PR and the roadmap captures every deferred finding. - -## 9. Pillar: Live VM runtime QA - -Pillar 8 audited feature *code* (read-and-reason) and shipped a first VM-boot smoke pass. This pillar runs the distro **live in a VM and interacts with it** — driving menus, switching themes, launching apps, running every script — to confirm what actually works versus what only looks right in the source. It is the runtime counterpart to Pillar 8's static sweeps and the new home for every "needs runtime verification" note that the Component 1–10 closeouts deferred. - -### Method (how to drive the VM) - -- **Build + boot.** `nix build .#nixosConfigurations.default.config.system.build.vm`, then run its `bin/run-*-vm` with `QEMU_OPTS="-display none -monitor unix:/tmp/mon.sock,server,nowait -vga none"`. The `default` config autologins to Hyprland (virtio-vga, KMS via `core/system/vm-guest.nix`). -- **Observe.** Capture the framebuffer with the QEMU monitor's `screendump /path.ppm` over the monitor socket (`echo "screendump …" | socat - unix-connect:/tmp/mon.sock`), convert PPM→PNG, and eyeball. Crop regions for legibility. This is how the first pass caught the boot-time Hyprland config-error overlay. -- **Drive.** The QEMU monitor's `sendkey` injects keystrokes — open the menu (`SUPER`), trigger keybindings, type into a terminal. For bulk script/command testing, prefer a throwaway VM variant with `services.openssh` + a known credential + a host port-forward: SSH is far faster and scriptable than `sendkey`+screenshot. Add it to a test overlay, never to the shipped config. -- **State.** Delete the VM's `*.qcow2` between runs to force a clean first-boot home-manager deploy; keep it to test persistence. - -### Components (each is one batch — "do it, then look") - -1. **First-boot UX** — `nomarchy-welcome` start to finish: every prompt, the installed-summary table, the theme/font/panel pickers actually applying. (First pass fixed the `compgen` bug here.) -2. **Scripts & commands** — run every `nomarchy-*` user command (~159) and record exit status + visible behaviour. Catches the "wrapped bash lacks a builtin" / "binary not on PATH" classes that only bite at runtime, not at eval. -3. **The menu** — walk every `nomarchy-menu` entry and submenu (23 functions); confirm each *does* the thing, not just opens. -4. **Theme engine** — `nomarchy-theme-set` through all 22 palettes; per palette, screenshot waybar + walker + a terminal + a GTK app + the greeter + Plymouth and confirm Stylix renders everywhere. Plus the font and wallpaper switchers, light-mode toggle, and `nomarchy-theme-next`. -5. **Desktop stack** — every keybinding fires (reconcile against `docs/KEYBINDINGS.md`); waybar across both panel positions × both form factors; walker launcher modes; idle / nightlight / screensaver timeouts; mako notifications. -6. **Apps** — launch each `features/apps/*` app; confirm it opens and is themed. -7. **Options loop** — for each `nomarchy.*` option: set → rebuild → observe the claimed effect → unset → observe it's gone. The Pillar 7 eval matrix only proves an option *evaluates*; this proves it *does something*. -8. **Installer** — boot `installerVm`; drive `install.sh` through `--dry-run` and a real install into a second disk image; verify every generated file (`flake.nix`, `system.nix`, `home.nix`, `hardware-selection.nix`, `state.json`). -9. **Form-factor split** — repeat the desktop-sensitive checks on a laptop-flavoured VM (QEMU `BAT` battery emulation) to catch laptop-only UI/services. - -### Deliverable - -- Per batch: a short report — what was run, what worked, what broke, what was fixed inline (with screenshot/log evidence where it matters), what was deferred as a new row. -- Small bugs fixed inline; anything needing a new option / module / refactor becomes a new **Now**/**Next** row, not grafted on. -- Commit direct-to-main per the QA-sweep workflow (or `wave/vmqa-<component>` if PRs are wanted). Doc updates ride with the change per `docs/AGENT.md` §5.4. - -Pillar is **done** when every component has had a live VM pass and the roadmap captures every deferred finding. - -## 10. Process notes - -- **Branch naming:** `wave/<pillar>-<short-slug>`. Examples: `wave/audit-pkg-scripts`, `wave/installer-disk-metadata`, `wave/laptop-preset`. -- **One PR per audit batch.** Reference rows in `docs/SCRIPTS.md`. Smaller PRs review faster. -- **Living roadmap.** When an item ships, move it to the **Shipped** section at the bottom of this file rather than deleting it. Future-us gets a free changelog. -- **Plan files live separately.** Detailed implementation plans (the per-feature design docs Claude writes in plan mode) belong under `~/.claude/plans/` per session, not in the repo. The roadmap is the durable reference; plan files are working notes. -- **Don't widen scope mid-PR.** If the audit reveals a missing feature, file a new roadmap row, don't graft it onto the current PR. - -## Shipped - -(Move items here when they land — keep them brief, link the commit/PR.) - -- _2026-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). -- _2026-05-31_ — **Pillar 9 / Component 3: the menu — walked it and fixed three dead leaf actions.** Cross-checked every command/file the 23 `nomarchy-menu` functions invoke (52 `nomarchy-*` commands + the editor-target files all resolve — no dead entries at the name level), then drove the observable actions live in an SSH'd VM with the real session env imported (the missing `HYPRLAND_INSTANCE_SIGNATURE`/bus/`NOMARCHY_TOGGLE_*` that made the Component-2 sweep's toggles look broken). Working: idle/waybar/screensaver/nightlight toggles (state.json writes), gaps toggle, monitor-scaling cycle (1.6→2.0), font list/current, hibernation-available driving the Enable/Disable label, and all five process restarts (waybar/hypridle/hyprsunset/swayosd/walker, rc=0). **Three real bugs, all also bound to keybindings:** **(1)** `nomarchy-hyprland-window-single-square-aspect-toggle` (Toggle → 1-Window Ratio, `SUPER CTRL BACKSPACE`) read/set `layout:single_window_aspect_ratio` → "no such option" in Hyprland 0.52.1; the option lives under `dwindle:`. Fixed; verified it now flips `[0,0]↔[1,1]` live. **(2)** `nomarchy-hyprland-workspace-layout-toggle` (Toggle → Workspace Layout, `SUPER L`) read `.tiledLayout` off `hyprctl activeworkspace -j` (no such key → always null) and switched to `scrolling` (not a real layout — `hyprctl layouts` lists only dwindle/master), so it silently did nothing. Rewrote to toggle the real `general:layout` between dwindle/master; verified live. **(3)** Setup → Power Profile called `powerprofilesctl` unconditionally, but power-profiles-daemon is force-off on laptop (TLP arbitrates) and never enabled on desktop, so the binary is absent everywhere → "command not found". Gated the entry behind `command -v powerprofilesctl`. Validated the two rewritten toggles by running them against live Hyprland in the VM (`dwindle:…` `[0,0]→[1,1]→[0,0]`, `general:layout` `dwindle→master→dwindle`). Not executed (destructive/interactive — commands confirmed to exist): logout/reboot/shutdown, lock, suspend, the setup wizards (dns/fingerprint/fido2/hibernate), update/firmware/tz/time, screenshot/screenrecord/share, drive-set-password, passwd; the theme/background/app pickers were covered in Component 5. _Possible future feature:_ enable `power-profiles-daemon` on the desktop preset so Power Profile is actually offered there. -- _2026-05-31_ — **Pillar 9 / Component 2 follow-up: grounded + fixed the three candidate bugs from the script sweep.** All three confirmed real. **(1)** `nomarchy-refresh-config` was dead on every system — it read `/etc/nixos/nomarchy/{core/home/config,features}` (the source tree never lands there; it's only at `/etc/nomarchy`, and only on VM-guest/live ISO) and fell back to `~/.local/share/nomarchy/config`, which nothing deployed even though `SKILL.md` documents it as the stock-config path. Deployed the pristine `core/home/config` tree to `~/.local/share/nomarchy/config` via `xdg.dataFile` and rewrote the script to read from there, so it works on any system without a source checkout (its only live caller, `nomarchy-refresh-fastfetch` → `fastfetch/config.jsonc`, now succeeds); fixed two stale `SKILL.md` examples that pointed at non-existent stock paths (`waybar/`, `hypr/`). Restoring arbitrary `features/`-owned configs (waybar, the full hypr tree) has no single stock mirror — deferred as a possible future feature, not grafted on. **(2)** `nomarchy-docs-scripts` shipped a stale, divergent copy in `features/scripts/utils/` (in the user `nomarchy-system-scripts` package) alongside the canonical `bin/utils/` dev tool — outside the repo it errored (`set -u` + `repo_root=…/../..`). Deleted the duplicate; grounding it surfaced **the identical bug** in `nomarchy-docs-keybindings` (a stale `features/` copy still referencing `tiling.conf` vs the canonical `tiling-v2.conf`), deleted too. Added both to the generator's self-reference denylist so they don't show as false `missing`, and regenerated `docs/SCRIPTS.md`. **(3)** Home `~/.config/nomarchy/state.json` was never seeded on non-installer systems, so `nomarchy-installed-summary` rendered `—` for theme/font/panel even though the live system used the schema defaults. Added an idempotent home-manager activation seed in `core/home/state.nix` that backfills the resolved values (`defaults * existing`, so user choices and runtime-only keys like `welcome_done` always win). Verified: `flake check` + full eval matrix clean, `refresh-config` happy/error paths and the seed deep-merge proven by hand. Activation file-write on a real boot is the one piece left for a live VM pass. -- _2026-05-31_ — **Pillar 9: chasing a D-Bus error uncovered the whole app list being dropped.** A `DBus.Error … name is not activatable` seen during the menu run traced back to `notify-send` failing because mako wasn't running. Reproduced exactly via `uwsm-app -- mako` → "Command not found: mako". Root cause was far bigger than the notification daemon: `home.packages` in `features/default.nix` was wrapped in `lib.mkDefault`. `home.packages` is a list, and `filterOverrides` keeps only the highest-priority definitions — so the moment any other module set `home.packages` at normal priority (`features/scripts/default.nix`'s `[ nomarchy-scripts ]`, the installer's profile packages), the **entire curated mkDefault list was silently discarded**. Every install was missing firefox, thunar, imv, mpv, swww, **mako** (→ broken notifications), **hyprlock** (→ broken lock screen, the "hyprlock not found" flagged in the earlier script sweep), and rofi — and the drop also hid a stale `rofi-wayland` reference (merged into `rofi` upstream) that only errored once the list went live. Removed the `mkDefault` so the list merges (`home.packages` 31 → 52 pkgs) and fixed `rofi-wayland` → `rofi` (`1117dcf`). Verified on a rebuilt VM: mako installs + runs + owns `org.freedesktop.Notifications`, hyprlock/firefox present, and `nomarchy-toggle-waybar` (the command that errored) runs clean with no D-Bus error. Lesson for `docs/AGENT.md`-style guardrails: never wrap a *list/attrset* option in `lib.mkDefault` expecting downstream override — it makes the whole definition vanish when anything else contributes; `mkDefault` only makes sense on scalar leaves. - -- _2026-05-31_ — **Pillar 9 / Component 5: walker menus verified, two real bugs fixed.** Drove every walker menu in a VM (theme picker, background selector, app launcher). Found two bugs, both invisible to eval/CI: **(1)** the elephant lua menu providers (`nomarchy_themes.lua` → `menus:nomarchythemes`, `nomarchy_background_selector.lua` → `menus:nomarchyBackgroundSelector`) were deployed via the bulk nomarchy config to `~/.config/nomarchy/default/elephant/` — outside elephant's provider search path — so elephant never registered them and the theme picker / background selector returned "No Results" (used by `nomarchy-theme`, `nomarchy-wallpaper`, and the `nomarchy-menu` Style submenu). Moved them into `features/apps/elephant/config/menus/` → `~/.config/elephant/menus/` (`c831b01`); verified on a fresh boot via `elephant listproviders` and the rendered theme picker showing all 21 palettes **with per-theme preview images**. **(2)** Even once registered, the background selector still returned "No Results" because its `GetEntries` ran `find <dir> -type f`, but home-manager deploys the per-theme backgrounds as nix-store symlinks and `-type f` doesn't follow symlinks — added `-L` to match `nomarchy_themes.lua` (`c5fe0e0`); proven in the VM (`find -type f` → 0, `find -L` → the backgrounds). App launcher (desktopapplications provider) confirmed working — lists apps with icons. Note on method: walker renders reliably when launched from a terminal in-session; the `sendkey` keybinding/`esc` path is flaky headless. - -- _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 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. - -- _2026-05-29_ — **Pillar 7: opt-in toggle eval matrix in CI.** `nix flake check --no-build` only evaluates the four default `nixosConfigurations`/`homeConfigurations`, so any eval-time bug that *only* fires when a `nomarchy.*` toggle is flipped sailed straight through — exactly how the vscode option rename and the impermanence systemd-stage-1 assertion both reached `main` undetected. New `bin/utils/nomarchy-eval-matrix` layers each opt-in scenario (16 toggles: impermanence single/multi, laptop/desktop presets, accessibility, gaming, hybridGPU, hibernation, docker, fwupd, overrides, panel position, keymap variant, toggles-off — plus home + system halves) onto `nixosConfigurations.default` via `extendModules` and forces `system.build.toplevel.drvPath` (which forces the assertion checks); it also forces all 21 palettes through the system-side Plymouth `base00`→RGB-float math that the home-only `allThemeVariants` never reaches, and both standalone `homeConfigurations`. Per-scenario results come from `builtins.tryEval` so one failure doesn't mask the rest — verified the harness goes red on both a failed assertion (re-injected `boot.initrd.postDeviceCommands`) and a nonexistent-option ref, and green on the current tree (all 39 scenarios pass). Wired into `.gitea/workflows/check.yml` as a step after `flake check`, with `jq` supplied via `nix shell` so it doesn't depend on the runner image. `docs/AGENT.md` §5.5 now instructs: when you add or rename an opt-in `nomarchy.*` option, add a matching scenario to the script's `toggleScenarios` attrset. - -- _2026-05-29_ — **Impermanence rollback no longer fails to build.** `core/system/impermanence.nix` implemented the Erase-Your-Darlings root wipe via `boot.initrd.postDeviceCommands`, but `themes/engine/plymouth.nix:63` sets `boot.initrd.systemd.enable = true` distro-wide (Plymouth is imported unconditionally from `core/system/default.nix`), and systemd stage-1 initrd hard-rejects `postDeviceCommands` with a failed assertion. Net effect: **every** install that flipped `nomarchy.system.impermanence.enable = true` — including any user who picked impermanence at the installer prompt — produced a config that wouldn't evaluate, let alone boot. Converted the rollback into a `boot.initrd.systemd.services.nomarchy-rollback` oneshot unit ordered `after = systemd-cryptsetup@${mainLuksName}.service` and `before = sysroot.mount`, with `boot.initrd.systemd.initrdBin` pulling in `btrfs-progs` / `coreutils` / `util-linux` / `findutils` (the systemd initrd doesn't ship them by default, unlike the old scripted initrd). Script body (timestamped `@` → `old_roots` move, 30-day recursive subvolume GC, `root-blank` → `@` snapshot) is unchanged. Verified the assertion is gone and `nixosConfigurations.default` + `impermanence.enable` evaluates fully via `extendModules`. **Runtime-verification caveat:** the boot-time wipe semantics (the `[[ ]]`/function/IFS shell idioms and the cleanup loop under the systemd initrd shell, plus correct ordering vs the LUKS mapping) can only be confirmed by a real wipe-boot cycle on an impermanence install — fold into the Pillar 8 punch-list. `docs/TROUBLESHOOTING.md` persistence-block line reference updated (46-72 → 91-120). - -- _2026-05-22_ — **VSCode theme extensions pinned for 10 marketplace palettes.** Before this fix, only the 6 palettes whose theme extensions ship in `pkgs.vscode-extensions` (catppuccin, catppuccin-latte, nord, tokyo-night, rose-pine, gruvbox) had working VSCode theming — every other palette had `workbench.colorTheme` set to a theme VSCode couldn't find, so it silently fell back to the built-in default. Including the default `summer-night` palette (sainnhe.everforest), which meant the default install had broken VSCode theming. Probed all 13 unique extensions against the VSCode marketplace extensionquery API; 10 exist (`sainnhe.everforest`, `shadesOfBuntu.flexoki-light`, `qufiwefefwoyn.kanagawa`, `oldjobobo.{lumon,miasma,retro-82}-theme`, `TahaYVR.matteblack`, `jovejonovski.ocean-green`, `monokai.theme-monokai-pro-vscode`, `Bjarne.white-theme`) and 3 don't (`Bjarne.{ethereal,hackerman,vantablack}-nomarchy` — unpublished custom Nomarchy themes; logged as a new Later row). Fetched the latest version + sha256 for each of the 10 via `nix store prefetch-file` against `https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${name}/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage`. Added a `marketplaceExtensions` list to `features/apps/vscode.nix` that wraps each entry in `pkgs.vscode-utils.extensionFromVscodeMarketplace { publisher; name; version; sha256; }` and concatenates with the existing nixpkgs-packaged list. Smoke-built `sainnhe.everforest` end-to-end to confirm the URL pattern + sha256 resolve. Documented the version-bump procedure in the module comment. `docs/OPTIONS.md` updated to drop the "still break" caveat for everything except the three Bjarne palettes. -- _2026-05-22_ — **Chromium static `Default/Preferences` deleted.** `features/apps/chromium/default.nix` was deploying a 204-byte static `Default/Preferences` via Home Manager symlink into Chromium's mutable profile directory. Chromium expects to write that file at runtime, so the symlink-into-store deployment was structurally broken (Chromium either fails to save, or replaces the symlink on first write — losing whatever the static file claimed to set). The contents (`extensions.theme.{use_system,use_custom} = false`, `browser.theme.{color_scheme,user_color} = 2`) are duplicates of the managed-policy intent in `core/system/browser.nix` (`BrowserThemeColor` from palette `base00`, `BrowserColorScheme` from `isLightTheme`) — and worse, the static `color_scheme = 2` hardcoded "dark" while the policy is dynamic, so a light-palette user would have hit a conflict. Removed the whole `features/apps/chromium/` directory (8 lines + the 204-byte file) and dropped the import from `features/default.nix`. Chromium theming continues to flow through the system-level managed policy, which is the canonical chromium-on-NixOS path. -- _2026-05-22_ — **Plymouth boot splash follows the active palette.** `themes/engine/plymouth/nomarchy.script` had `Window.SetBackgroundTopColor(0.101, 0.105, 0.149)` hardcoded — a Tokyo-Night-ish `#1a1b26` — and `nomarchy.plymouth` had a matching `ConsoleLogBackgroundColor=0x1a1b26`. Both were frozen regardless of `nomarchy.system.theme`. Replaced the literals with `@BG_R@`/`@BG_G@`/`@BG_B@`/`@BG_HEX@` placeholders; `themes/engine/plymouth.nix` now reads the active palette via `nomarchyLib.getPalette config.nomarchy.system.theme`, converts `palette.base00` into three 0.0–1.0 floats (Nix has no FP math, so `(byte * 1000) / 255` integer division formatted as `0.XXX`), and `sed`-substitutes during `installPhase`. Smoke-built the derivation against the default `summer-night` palette: emits `0.176, 0.207, 0.231` (matches `0x2d353b`) and `ConsoleLogBackgroundColor=0x2d353b`. Closes the "Plymouth theme variants per palette" Next-column item. -- _2026-05-22_ — **`nomarchy.overrides.*` loader implemented.** The option surface (`enable`, `paths`) had existed in `core/home/overrides.nix` since 2026-05-18 but did nothing — `paths` was a reserved attrset that never reached `xdg.configFile`. Now wired: every entry in `nomarchy.overrides.paths` substitutes the matching `xdg.configFile.<key>.source` at `lib.mkForce` priority, beating Nomarchy's own `lib.mkDefault` writes. Other fields on the original entry (`recursive` etc.) survive via the standard module-system merge. Picked the attrset model from the row's two options rather than runtime `~/.config/nomarchy/overrides/` directory discovery — Nix needs every managed file declared at evaluation time, and the attrset matches the explicit-config shape used everywhere else in the Nomarchy surface. `docs/OPTIONS.md` updated: both `overrides.{enable,paths}` entries gain real content + an example, the `configOverrides` row now contrasts bulk-vs-per-file accurately, and the "Where these are defined" footer drops the (reserved) tag. Closes the Next-column row. Unlocks the future replacement for the removed Setup→Config menu. -- _2026-05-22_ — **Accessibility home-side companion shipped.** New `nomarchy.accessibility.enable` home option (mirror of `nomarchy.system.accessibility.enable`) plus `core/home/accessibility.nix` that, when enabled, contributes a Hyprland `extraConfig` block via `lib.mkAfter`: slows `input.repeat_rate` to 25 (from 40) and `input.repeat_delay` to 1000 ms (from 600) so holding a key isn't a runaway machine-gun for low-mobility users, and binds `SUPER+ALT+S` to launch the Orca screen reader (the system preset already puts `orca` on PATH). The `mkAfter` priority guarantees the input slowdown wins over the templated `input.conf` defaults. Documented in `docs/OPTIONS.md`. The third item from the original Next row — a high-contrast palette — is split into its own Later row because it's a design task (24-colour WCAG AAA palette + icon family choice) that wants its own review. -- _2026-05-22_ — **Gaming preset: flathub remote registered automatically.** `services.flatpak.enable = true` (set inside `core/system/gaming.nix`'s `mkIf cfg.enable` block) shipped flatpak but didn't add any remotes — `flatpak install` and the Discover GUI returned empty results until the user ran the manual `flatpak remote-add` one-liner. nixpkgs has no declarative remote-add API. Added `systemd.services.nomarchy-flathub-init`: a `Type=oneshot`, `RemainAfterExit=true` unit that runs `flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo` after `network-online.target`. The `--if-not-exists` flag keeps it idempotent across reboots and re-runs. Lives under the gaming preset (where flatpak is wired today); lift to a dedicated module when another preset needs flatpak. Closes the "Gaming — declarative flathub remote" Next-column item. -- _2026-05-22_ — **`nomarchy-menu` "Setup → Config" submenu removed.** Of the nine entries, five (`hyprland.conf`, `hypridle.conf`, `hyprsunset.conf`, `walker/config.toml`, `waybar/config.jsonc`) opened Home-Manager-generated files that get clobbered on the next `home-manager switch`; two (`hyprlock.conf`, `swayosd/config.toml`) pointed at paths the modules don't deploy at all; `XCompose` was a HM-managed symlink to the Nix store (read-only). The "edit + restart" pattern was an Omarchy holdover that didn't translate to Nomarchy's declarative-first model. Removed `show_setup_config_menu` entirely and dropped the " Config" item from `show_setup_menu` (closes the case branch in the parent menu). Persistent settings now go through the matching `nomarchy.*` option in `/etc/nixos/home.nix` (or `system.nix`); when the `nomarchy.overrides.*` loader ships, the menu can come back routed through `~/.config/nomarchy/overrides/`. Side effect: `nomarchy-restart-xcompose` now `unused?` in `docs/SCRIPTS.md` (regenerated) — left as a future Pillar 3 cleanup, not widened into this PR. -- _2026-05-22_ — **`toggles.waybar` is now a Nix-level gate.** `features/desktop/waybar/default.nix` previously set `programs.waybar.{enable,systemd.enable} = lib.mkDefault true` unconditionally, so the bar came back on every rebuild/reboot regardless of `nomarchy.toggles.waybar`. The toggle script consumed `NOMARCHY_TOGGLE_WAYBAR` but only via runtime pkill/exec — purely session-local. Same shape as the just-fixed nightlight: both options now drive their `programs.X.enable` / `services.X.enable` symmetrically with `toggles.idle` → `services.hypridle.enable`. `programs.waybar.{enable,systemd.enable}` now follow `config.nomarchy.toggles.waybar`; the toggle script flips the running unit via `systemctl --user start/stop waybar.service` for instant feedback and writes `.waybar` back to `state.json` so the next rebuild realigns. `nix flake check --no-build` + `bash -n` clean. -- _2026-05-22_ — **Nightlight reconciled: hyprsunset gated on the toggle.** Path (b) from the Later row. `features/desktop/nightlight.nix` previously set `services.hyprsunset.enable = lib.mkDefault true` unconditionally and baked the temperature (4000K or a neutralising 6500K) into `extraArgs` at Nix-eval time, while the toggle script `pkill`'d the daemon on disable and `hyprctl dispatch exec hyprsunset --temperature 4000`'d a new one on enable — racing the systemd unit and hardcoding 4000K regardless of `nomarchy.nightlightTemperature`. Now: `services.hyprsunset.enable = lib.mkDefault config.nomarchy.toggles.nightlight` (symmetric with `services.hypridle.enable` ← `toggles.idle`), `extraArgs` always uses the option value, and the toggle script flips the running unit via `systemctl --user start/stop hyprsunset.service`, reads `nightlightTemperature` from `state.json` for the notification, and writes `.nightlight` back to `state.json` so the next rebuild realigns. Dropped the misleading "Always enabled, we control via IPC and state" comment. `nix flake check --no-build` + `bash -n` clean. -- _2026-05-22_ — **Installer keymap reaches Hyprland's Wayland session.** `core/home/config/nomarchy/default/hypr/input.conf` hardcoded `kb_layout = us`, so the installer's `services.xserver.xkb.layout` / `console.keyMap` writes only reached XWayland and the TTY — native-Wayland apps fell back to US. Option-route fix (path (a) from the Later row): added `nomarchy.keymap.{layout,variant}` to `core/home/options.nix` (defaults `"us"` / `""`); deleted the static `input.conf` from the bulk `nomarchy/` deploy; moved it into `core/home/configs.nix` as an explicit `xdg.configFile."nomarchy/default/hypr/input.conf".text = ''…''` that interpolates the option values. Installer's `home.nix` heredoc now writes `nomarchy.keymap = { layout = "$KEYMAP_LAYOUT"; variant = "$KEYMAP_VARIANT"; };` alongside `nomarchy.formFactor`, so a non-US install propagates the layout consistently to Hyprland, XWayland, and the TTY. Documented in `docs/OPTIONS.md`. `nix flake check --no-build` clean. -- _2026-05-22_ — **`flake.nix` palette imports consolidated through `nomarchyLib`.** `flake.nix` was re-importing `./themes/palettes` and recomputing `themeNames` via `builtins.attrNames` even though `lib/default.nix` already exports both. Two evaluations of the same data set with the same result today — drift risk tomorrow. Added `nomarchyLib = import ./lib { inherit lib; }` once in the outputs `let` and reused it via `inherit (nomarchyLib) themeNames;` for the `allThemeVariants` linkFarm. `lib/default.nix` is now the single source of truth for the theme list — modules that need palette data import `../../lib` the same way and resolve to the same evaluation. `nix flake check --no-build` clean. -- _2026-05-22_ — **`programs.uwsm` moved to `core/system/session.nix`.** The session-manager wiring (uwsm + the Hyprland Wayland-compositor entry that gives Hyprland a proper `graphical-session.target` so user services like `nomarchy-wallpaper`, walker and elephant chain off it) had lived in `core/system/virtualization.nix` by historical accident — loaded unconditionally on every install, nothing to do with libvirt/docker. Lifted into a dedicated `core/system/session.nix` and imported from `core/system/default.nix` between `systemd.nix` and `virtualization.nix`. `virtualization.nix` now contains only the libvirt + docker branches its filename implies. `nix flake check --no-build` clean. No behaviour change. -- _2026-05-22_ — **`themes/templates/*.tpl` pruned.** Deleted 9 of the 11 mustache templates after verifying their output paths are either preempted by Nix-side writes (`hyprland.conf.tpl` shadowed by `themes/engine/files.nix:100`; `kitty.conf.tpl` + `ghostty.conf.tpl` shadowed by the per-palette generators added in commit `8d3ce2d`), unread by anything (`hyprlock.conf.tpl`, `alacritty.toml.tpl`, `btop.theme.tpl`, `chromium.theme.tpl`, `swayosd.css.tpl` — the corresponding apps are themed via Stylix / declarative Home-Manager options / the system policy module, not from the theme symlink), or orphaned (`hyprland-preview-share-picker.css.tpl` lost its consumer when the share-picker dir was deleted in `20de3d4`). Only `obsidian.css.tpl` (consumed by `nomarchy-theme-set-obsidian` to seed every Obsidian vault's theme) and `keyboard.rgb.tpl` (consumed by `nomarchy-theme-set-keyboard-asus-rog` to set the ROG keyboard tint) stay. Rewrote Step 6 of `docs/creating-themes.md` to describe the two remaining templates explicitly and corrected a long-standing path bug ("`~/.config/nomarchy/themed/`" → "`~/.config/nomarchy/themes/templates/`" — the script actually reads the latter). `nix flake check --no-build` clean. -- _2026-05-22_ — **Pillar 4: "What's installed?" first-boot summary.** New `nomarchy-installed-summary` script renders a markdown table (via `gum format`, plain fallback) showing the install shape the user should verify before customising: theme / font / panel position (read from `~/.config/nomarchy/state.json`), timezone / DNS / hybrid-GPU (read from `/etc/nixos/state.json`), form factor (`BAT*` sysfs check — same signal the installer uses), software profiles (heuristic via presence of marker binaries: `docker` → Dev, `steam` → Gaming, `libreoffice` → Office, `obs` → Media, `rg` → CLI Utils), FDE status (any `crypt` entry in `lsblk`), and the drive layout (filtered `lsblk -no NAME,SIZE,TYPE,MOUNTPOINT`). `nomarchy-welcome` now calls it as Step 0 (gated on a `gum input` so the user acknowledges before customisation rewrites anything) and the same command works standalone from any terminal. No installer-side changes — the script is fully self-contained against existing state files and live introspection. Closes the "Installer: What's installed? summary on first boot" Now-column item. -- _2026-05-21_ — **Pillar 8 / Component 9 (ISOs): closeout — Pillar 8 code-audit phase complete.** Code-review-shaped sweep over `hosts/{nomarchy-installer,nomarchy-live}.nix`, the `installation-cd-minimal`/`installation-cd-graphical-base` module chain, and the four ISO build/test scripts. Two minor fixes inline: `nomarchy-build-iso` and `nomarchy-build-live-iso` both ran under `set -e` but then wrapped `nix build` in an `if [ $? -eq 0 ]` block — the `else` branch printing "Error: ISO build failed." was unreachable because `set -e` aborts before the conditional. Removed the dead branches (behaviour identical: the user sees `nix build`'s own error and the script exits). Regression-class check (`hardware-db.sh` precedent): cross-referenced every tool `install.sh` calls against the installer host's `environment.systemPackages` chain — `gptfdisk` (sgdisk) is provided by upstream `profiles/base.nix:21` which `installation-cd-base.nix` chains, `jq` is wrapped in the `nrun` nix-run fallback, and every other direct call (`wipefs`, `dd`, `parted`, `partprobe`, `cryptsetup`, `disko`, `nixos-{install,enter,rebuild}`, `loadkeys`, `timedatectl`, `nmtui`) resolves via either the explicit host packages or the standard base. `nomarchy-live` host shape verified: multi-GPU initrd modules + Xwayland video drivers cover both real hardware and QEMU; auto-login + passwordless sudo + helpful TTY MOTD + Hyprland on-boot exec to a terminal at the install command. `nomarchy-test-live-iso` walks four OVMF candidate paths with KVM detection. With this entry, every code-shaped audit in Pillar 8 has shipped (Components 1–10); the Now-column "Full QA audit" item moves out, replaced by a runtime-verification punch-list entry covering the cross-component "needs runtime verification" notes from each closeout. -- _2026-05-21_ — **Pillar 8 / Component 8 (Scripts runtime behavior): closeout.** Code-review-shaped sweep over `features/scripts/utils/nomarchy-menu` (382 lines, 23 submenu functions), every script referenced from those submenus, the schema↔script field-name cross-check, and cross-cutting typo/stale-reference patterns. Four real fixes inline: **(1)** `nomarchy-menu:70` — "Learn → Nomarchy" still called `nomarchy-launch-webapp https://learn.omacom.io/2/the-nomarchy-manual` (an upstream Omarchy URL — the same one fixed in `nomarchy-manual` back on 2026-05-18). Now calls `nomarchy-manual`, which opens the local docs index. **(2)** `nomarchy-menu:179` — "Style → Hyprland" opened `~/.config/hypr/looknfeel.conf`, a path nothing deploys; the actual file lives at `~/.config/nomarchy/default/hypr/looknfeel.conf` (sourced via the chain from `nomarchy.conf`). Updated the path. **(3)** `nomarchy-menu:258` — `*Overrides*) xdg-open ~/.config/nomarchy/overrides/` case branch with no matching menu option, dead code anticipating the still-unimplemented `nomarchy.overrides.*` loader. Removed (will reappear with the option when the loader ships). **(4)** `nomarchy-theme-bg-next:12` — `jq -r '.theme // "nord"'` defaulted to `"nord"` if `.theme` was missing, while `lib/state-schema.nix:17` defines `"summer-night"` as the schema default. On a fresh-or-empty `state.json` the script looked for backgrounds under `palettes/nord/` while the rest of the system treated `summer-night` as active. Matched to the schema default. Cross-cutting sweeps came back clean: no `$NN[A-Z]+` env var typos elsewhere (the prior pair fixed in `40b6212` was the lot), no references to scripts deleted in earlier Pillar 3 batches (`nomarchy-restart-{hyprctl,mako,tmux}`, `nomarchy-battery-present`, `nomarchy-sudo-keepalive`, `nomarchy-rollback`, `nomarchy-snapshot`, `nomarchy-migrate-state`, `nomarchy-config-direct-boot`, `nomarchy-npx-install`, `nomarchy-webapp-handler-{hey,zoom}`), no stray `omarchy`/`omacom` strings outside historical roadmap entries, and every `state.json` field-write resolves against `lib/state-schema.nix` (or the documented off-schema `welcome_done`). One UX-shaped pattern bug logged separately to Later: `show_setup_config_menu` edits Nix-managed files that get clobbered on the next `home-manager switch`. Runtime verification (run every user-visible menu entry and confirm it does the thing) remains on the user. -- _2026-05-21_ — **Pillar 8 / Component 7 (Theme engine + palettes): closeout.** Code-review-shaped sweep across `themes/engine/{stylix,stylix-compat,loader,files,scripts}.nix`, the 23 theme-engine scripts, and the 21 palettes' file completeness. Three real fixes inline + targeted dead-surface cleanup: **(1)** `nomarchy-theme-set` printed a warning when the named theme directory didn't exist but continued executing — wrote the bad name into `state.json` and ran `nomarchy-env-update` on a broken state. Now `exit 1` after the warning. **(2)** `nomarchy-theme-bg-set` (called by the walker background-selector menu and by the `nomarchy-wallpaper` CLI) updated the live `~/.config/nomarchy/current/background` symlink + restarted swaybg but never wrote `state.json` — so every wallpaper picked via either path silently reverted to the theme default on the next `home-manager switch` (`themes/engine/files.nix` re-resolves `nomarchy.wallpaper` at every rebuild). Now writes the chosen path into `state.json.wallpaper`, mirroring `nomarchy-theme-bg-next`. Added a file-exists check so a bogus path fails loudly instead of leaving a dangling symlink + a crashed swaybg. **(3)** Palette tree dead-surface cleanup: deleted `themes/palettes/{flexoki-light,lumon,retro-82,rose-pine}/apps/chromium.theme` (9-byte RGB strings nothing reads — chromium is themed via managed policies in `core/system/browser.nix`, not per-palette files) and `themes/palettes/summer-day/apps/kitty/{kitty.conf,everforest-light.conf}` (a 76KB stray kitty config at the wrong nested path, superseded by the `kitty.conf` generator added in `8d3ce2d`). Total: 6 files / 2210 lines. Updated the misleading comment in `nomarchy-themes-prebuild` ("the installer wires this up") to reflect reality (the installer only tips the user to run it). Updated the `themes/templates/*.tpl` Later row with a fact-check + concrete categorisation — the templates ARE consumed by `nomarchy-theme-set-templates`, but most write to paths nothing reads or are now superseded by Nix-side generators. Palette completeness matrix: all 21 palettes have `colors.toml`, `backgrounds/`, `icons.theme`, and `apps/`; 5 carry the `light.mode` marker (catppuccin-latte, flexoki-light, rose-pine, summer-day, white); only tokyo-night ships `keyboard.rgb` for the ASUS ROG path, and the keyboard-set chain isn't wired into `nomarchy-theme-set` so it stays manual — niche enough to leave. Runtime verification (switch through all 22 palettes and eyeball SDDM + Plymouth + GTK + Qt + terminals + browsers + waybar + walker rendering) remains on the user. -- _2026-05-21_ — **Pillar 8 / Component 6 (Apps): closeout.** Code-review-shaped sweep over `features/apps/{alacritty,btop,chromium,elephant,ghostty,kitty,lazygit,opencode,swayosd,tmux,vscode,walker}`. Three real theming bugs fixed inline: **(1)** `features/apps/kitty/config/kitty.conf:1` and `features/apps/ghostty/config/config:2` referenced palette-specific include files (`~/.config/nomarchy/current/theme/{kitty,ghostty}.conf`) that didn't exist for any of the 22 palettes — kitty include failed silently, ghostty's was optional (`?`-prefix), and both terminals rendered with their built-in defaults regardless of the active Nomarchy palette. Stylix's `kitty.enable = true` was a no-op because the module uses `xdg.configFile` instead of `programs.kitty`; ghostty has no Stylix target. Added theme-engine generators in `themes/engine/files.nix` mirroring the existing `waybar.css` pattern, mapping `palette.base*` to kitty/ghostty color directives. **(2)** `features/apps/btop/config/btop.conf:5` set `color_theme = "current"` but `themes/engine/loader.nix:72` deploys the active palette's btop theme to `~/.config/btop/themes/nomarchy.theme` — name mismatch, btop fell back to its built-in Default theme on every palette. Renamed to `"nomarchy"`. **(3)** `programs.vscode.profiles.default.userSettings.workbench.colorTheme` was set unconditionally from `themes/palettes/<theme>/apps/vscode.json`, but the matching theme extensions were bundled with `devExtensions` (default `false`) — so VSCode silently fell back to its built-in theme out of the box on every palette. Split `themeExtensions` (always-on, covers the 6 palettes whose theme extension is in nixpkgs) from `devExtensions` (opt-in). The remaining 15 palettes — including the default `summer-night` (`sainnhe.everforest`) — still break because their theme extensions aren't packaged in nixpkgs; logged as a new Later row. Chromium static `Default/Preferences` symlink already had an open Later row; verified the file's contents are duplicate of the managed-policy intent in `core/system/browser.nix`, so the existing entry's hypothesis is correct — left for the user to greenlight deletion. alacritty (Stylix-themed via `programs.alacritty.settings`), elephant (no UI), swayosd (base16 inline), walker (covered in Component 5), lazygit + tmux (terminal ANSI inheritance, transitively fixed by the kitty/ghostty changes), and opencode (minimal opt-in config) are healthy. Runtime verification (launch each app on each palette and eyeball the theming) remains the user's responsibility. -- _2026-05-21_ — **Pillar 8 / Component 5 (Desktop stack): closeout.** Code-review-shaped sweep over Hyprland, waybar, walker, hypridle, hyprsunset, mako, KEYBINDINGS.md (the runtime-rendering subset — waybar across panel positions × form factors × all 22 palettes, walker launcher modes, hypridle timeout feel — stays on the user). Five real bugs fixed inline: **(1)** 9 of 17 `~/.config/nomarchy/default/hypr/apps/*.conf` window-rule files were deployed but never sourced, including `system.conf` (the `tag +floating-window` rules every TUI helper class relies on + `class:org.nomarchy.screensaver` fullscreen rule that hypridle's 150s on-timeout depends on) and `pip.conf` (the PiP pin/size rules). `apps.conf` now sources all 17. **(2)** Two `$NNOMARCHY_TOGGLE_*` typos (double-N) in `nomarchy-menu:330` and `nomarchy-launch-screensaver:16` made `toggles.suspend` and `toggles.screensaver` vacuous — Suspend always showed in the system menu and the screensaver always launched at idle regardless of the documented option. **(3)** 4 broken per-palette waybar `style.css` overrides (`catppuccin`, `lumon`, `nord`, `retro-82`) fully replaced the default style with 2–14 lines of only `@define-color` declarations — picking those palettes produced a waybar with zero structural styling. Default style already imports per-palette colors via `themes/engine/files.nix`-generated `theme/waybar.css`, so deletion restores correct rendering; `summer-day`/`summer-night` kept as legitimate 100+-line redesigns. **(4)** `core/home/config/nomarchy/default/hypr/{bindings,plain-bindings}.conf` were explicitly-labeled deprecated files sourced by nothing (plain-bindings.conf referenced undefined `$terminal`/`$browser`/etc. Hyprland vars) — deleted; `docs/SCRIPTS.md` regenerated to drop stale callers and incidentally corrected 4 Origin columns whose scripts moved from `core/system/scripts/` to `features/scripts/utils/`. **(5)** Mako post-fix (commit `2a301a0`) verified: deployment + the 4 referenced scripts (`nomarchy-notification-dismiss`, `nomarchy-launch-wifi`, `nomarchy-launch-floating-terminal-with-presentation`, `nomarchy-menu-keybindings`) all resolve. Two structural inconsistencies logged to Later: keymap routing (already in 72f7e7b) and the new hyprsunset toggle-vs-systemd reconcile. `KEYBINDINGS.md` regenerated with zero diff — generator already covers both binding source locations. Runtime verification (boot live ISO, eyeball waybar/walker/screensaver flows across panel positions and palettes) remains the user's responsibility before declaring Component 5 fully closed. -- _2026-05-18_ — Hardware DB correctness pass + ROG Ally support + CI lint. Audited every `nomarchy-hardware-db` entry against `inputs.nixos-hardware.nixosModules` and found **21 of 43 entries (49%) referenced modules that don't exist** — `microsoft-surface-pro-8`, `lenovo-thinkpad-x1-carbon-gen11`, `framework-13-11th-gen-intel`, etc. were all eval-time failures waiting for a real user. Rewrote the DB to use only valid module names: Framework gens dropped the "13-" prefix in nixos-hardware (`framework-11th-gen-intel`, not `framework-13-11th-gen-intel`); ThinkPad X1 modules are `x1-Nth-gen`, not `x1-carbon-genN`; Surface Pro 6/7/8/10 all share `microsoft-surface-pro-intel`; Surface Book / Intel Surface Laptop have no module (rows dropped, generic detection still emits sensible `common-pc-laptop` + cpu/gpu). Added matchers for **ROG Ally** (RC71L / RC72LA / "ROG Ally" via `asus-ally-rc71l`). Documented Steam Deck and Snapdragon X as nixos-hardware-unsupported in a footer comment (Steam Deck → Jovian-NixOS; Snapdragon X → installer is x86_64 only). Added a CI step (`.forgejo/workflows/check.yml`) that fails on any DB entry whose module name isn't in `nixos-hardware.nixosModules` — closes this regression class. -- _2026-05-18_ — `nomarchy-manual` re-targeted at local docs. The script's `xdg-open` previously pointed at `https://learn.omacom.io/2/the-nomarchy-manual` — an upstream Omarchy URL that opened an unrelated page when users hit the menu's Help entry. Now opens `~/.local/share/nomarchy/README.md` (the local docs index per `SKILL.md`'s "Out of Scope" note), with a `notify-send` fallback if the source tree isn't synced. -- _2026-05-18_ — Docs hygiene: STRUCTURE.md "Root Directory" + Pillar 6 reality-check. `docs/STRUCTURE.md` listed three top-level files that don't exist (`GEMINI.md`, root-level `STRUCTURE.md`, `TODO.md`) — replaced with an accurate root listing plus a `docs/` sub-tree that names every doc. Pillar 6 in this file had `nomarchy-welcome`, `docs/TROUBLESHOOTING.md`, and the "docs index" bullet still marked Next despite all three shipping on 2026-04-26 — moved to `(Shipped)`. `nomarchy-manual` bullet's "orphaned reference today" claim was stale (the script is called from `nomarchy-menu` and `nomarchy-theme-install`); rewritten to reflect the real remaining issue — its hardcoded `xdg-open https://learn.omacom.io/2/the-nomarchy-manual` is an Omarchy URL. -- _2026-05-18_ — Installer state.json is now schema-driven. Replaced the heredoc in `installer/install.sh` that hardcoded the JSON literal (theme/dns/wifi/features/etc.) with a `nix eval` of `lib/state-schema.nix`'s `system` block, overlaid with the installer-chosen timezone. Closes the last source-of-truth split after the centralization batch — adding a new default in the schema now reaches the installer with no further plumbing. Output is identical modulo alphabetical key ordering (Nix's `builtins.toJSON` sorts keys; toggle scripts read/write via `jq` so it's invisible to them). Dry-run path unchanged (still bind-mounts a fake `/mnt` so the generator's absolute paths resolve correctly). `bash -n` + `shellcheck --severity=error` clean. -- _2026-05-18_ — Complete the hybrid-GPU wiring + fix unoverridable state-derived options. Two related fixes shipped together. **(1)** `nomarchy.system.features.hybridGPU = true` now wires the full NVIDIA driver stack (`services.xserver.videoDrivers = ["nvidia"]`, `hardware.graphics.{enable,enable32Bit}`, `hardware.nvidia.{modesetting,powerManagement}.enable`, `package = nvidiaPackages.stable`, `boot.kernelParams += "nvidia-drm.modeset=1"`) — was previously enabling only `supergfxd` mode-switching while leaving the system with no NVIDIA driver loaded, so mode switches silently no-op'd. All knobs use `lib.mkDefault` so a downstream `system.nix` can pin a beta driver, flip to the open kernel module, etc. Bus-ID prime config (per-machine) stays user-supplied — `docs/OPTIONS.md` has the full recipe. **(2)** Both `core/system/state.nix` and `core/home/state.nix` now use `lib.mkDefault` on every state.json-derived assignment, fixing a class of "I set X in my system.nix but it doesn't take effect" bugs (the state-derived value was at default priority and conflicted with the user's same-priority override). Side-effect cleanup: `core/system/state.nix` now also reads from `lib/state-schema.nix` like `core/home/state.nix` does, completing the schema-centralization started two batches ago. Verified `nix flake check` + an override test that flips hybridGPU via an overlay and confirms the entire driver stack engages. -- _2026-05-18_ — Pillar 4: pre-flight resume polish. Fixed four resume-flow gaps in `installer/install.sh`: (1) `--resume` with a missing state file now errors loudly with a tmpfs explanation instead of silently falling through to a fresh prompt cycle (the most common operator confusion was "rebooted, forgot tmpfs eats /tmp/, watched the installer start over without realising"); (2) on resume, the saved target drive is validated as a block device before any disk-phase step runs — catches the live-ISO USB-unplugged / non-deterministic /dev/sdX class of mid-install failures; (3) `save_state` now stamps an ISO-8601 timestamp and `load_state` shows a `(saved Xm ago)` banner plus a `Target: /dev/X → user @ host` summary line, so the user can `Ctrl-C` if they're resuming onto the wrong host before any destructive prompt fires; (4) `--help` now documents the tmpfs limitation. `shellcheck --severity=error` passes. -- _2026-05-18_ — Declarative-state defaults centralization. Made `lib/state-schema.nix` the single source of truth for every state-default that previously lived in three places (the schema itself, `core/system/options.nix` / `core/home/options.nix` `default = …` clauses, and `core/home/state.nix` `or …` fallbacks). Replaced ~25 hardcoded literals with `schema.<scope>.<key>` reads. Side-effect: fixed a lingering bug where `core/home/options.nix:theme` still defaulted to `"summer-night"` after the system-side was moved to `"nord"` — half the codebase's home option resolved to the wrong theme when state.json was missing/blank. `nix flake check --no-build` confirms zero semantic change for every other field. Doesn't touch the installer-written `state.json` (separate batch — needs schema → JSON generation). -- _2026-05-18_ — Pillar 7 first step: Forgejo Actions CI (eval + lint). New `.forgejo/workflows/check.yml` runs on every push to `main` and every PR: (1) `nix flake check --no-build` to catch eval regressions, (2) `bash -n` + `shellcheck --severity=error` over every `nomarchy-*` bash script (whole-tree, not just changed files — gates branches that bypass the pre-commit hook), (3) `docs/SCRIPTS.md` drift check (fails loudly if a script change didn't regenerate the audit doc). All three checks pass locally on the current tree. Activation requires enabling Actions on the Forgejo repo and registering a `forgejo-runner`; the workflow itself is dormant until then. ISO build job is intentionally deferred — needs a binary cache (Cachix/Attic) to be tractable. -- _2026-05-18_ — **Pillar 3 Phase B: complete.** Final batch (restart/sudo/theme/misc clusters) cleared the last 13 `unused?` rows. Deleted five truly dead scripts: `nomarchy-restart-{hyprctl,mako}` (theme switching calls `hyprctl reload`/`makoctl reload` directly now), `nomarchy-restart-tmux` (one-liner of marginal value), `nomarchy-battery-present` (battery monitor checks `/sys/class/power_supply/BAT*` inline), `nomarchy-sudo-keepalive` (intended-to-be-sourced building block with no users). Surfaced eight useful tools in `SKILL.md` so the audit catches them as `kept` and AI assistants can discover them: `nomarchy-restart-trackpad` (intel_quicki2c reload), `nomarchy-sudo-{passwordless-toggle,reset}`, `nomarchy-theme-{bg-install,refresh,remove}`, `nomarchy-refresh-fastfetch`, `nomarchy-windows-vm` (new Virtualization section). Final state: 159 scripts, all `kept`, `unused?` = 0, missing references = 0. -- _2026-05-18_ — Pillar 3 Phase B: webapp/tui/voxtype install-remove pair triage. Deleted two dead webapp URI handlers (`nomarchy-webapp-handler-hey`, `nomarchy-webapp-handler-zoom`) — no `.desktop` MimeType registration anywhere routed `mailto:`/`zoom:` URIs to them, so the handlers could never fire. Surfaced six useful CLI tools in `SKILL.md` "Common Tasks" so they're discoverable by AI assistants and tagged `kept` by the audit: `nomarchy-webapp-{remove,remove-all}`, `nomarchy-tui-{remove,remove-all}`, `nomarchy-voxtype-{install,remove}`. Script count 166 → 164; `unused?` 21 → 13. -- _2026-05-18_ — Pillar 3 Phase B: dead-code sweep (NixOS-irrelevant Omarchy ports). Deleted five scripts that duplicated NixOS-native facilities or referenced infrastructure Nomarchy doesn't ship: `nomarchy-rollback` (boot-menu generations + `nixos-rebuild rollback` already cover this), `nomarchy-snapshot` (used `snapper`; impermanence and BTRFS subvolumes are the Nomarchy answer), `nomarchy-migrate-state` (one-shot pre-unification migration, no current callers), `nomarchy-config-direct-boot` (added an EFI entry for a UKI we never build), and `nomarchy-npx-install` (Arch idiom — `nix-shell -p nodejs` is the NixOS path). Kept `nomarchy-build-iso` and `nomarchy-build-live-iso` and surfaced them in README §2 so the audit tags them `kept`. Script count 171 → 166. -- _2026-05-18_ — Pillar 3 Phase B: missing-references triage. (1) Wrote `themes/engine/scripts/nomarchy-theme-next` so `SKILL.md`'s documented "cycle to next theme" command resolves; (2) scrubbed three stale `nomarchy-dev-*` references from `core/home/config/nomarchy-skill/SKILL.md`; (3) added a line-context filter to both `nomarchy-docs-scripts` generators that drops `nomarchy-*` tokens appearing in Nix `pname`/derivation idents, `/tmp/` & `/etc/sudoers.d/` paths, `nixosConfigurations.*` / `packages.*` flake outputs, `mktemp -t` prefixes, systemd unit vars, `./result/bin/run-` binaries, and `docker` container references; (4) added a small token-level denylist for five residual non-script identifiers (`nomarchy-plymouth`, `nomarchy-sddm-theme`, `nomarchy-live`, `nomarchy-rev`, `nomarchy-windows`) that survive line filtering. `docs/SCRIPTS.md` "Missing references" section is now empty (was 15). -- _2026-05-04_ — Pillar 8: Distro Branding. (1) Scrubbed remaining "Omarchy" and "Spirit of Omarchy" references from README, scripts, and welcome wizard; (2) Updated `nomarchy-welcome` banner and `nomarchy-version` codename ("Sovereign"); (3) Verified existing `core/system/branding.nix` handles OS-release and bootloader labels; (4) Confirmed SDDM and Plymouth metadata are already Nomarchy-branded. -- _2026-05-04_ — Thorough Out-of-the-Box QA Audit. (1) Restored automatic wallpaper switching by removing image filters from deployed themes; (2) Fixed broken "Style" menu entries by creating missing `about.txt` and `screensaver.txt` branding files; (3) Cleaned up conflicting keybindings by removing deprecated `tiling.conf` and updating the doc generator; (4) Removed legacy Nord theme hack from `nomarchy-theme-set`; (5) Fixed JSON parse error in `summer-day` waybar theme. -- _2026-05-03_ — Fixed multi-disk LUKS/BTRFS boot hang. (1) Moved temporary LUKS keyfile to `/tmp/` so Disko correctly omits it from the runtime configuration; (2) Injected `x-systemd.requires` and `x-systemd.device-timeout=0` into BTRFS mount options to ensure all LUKS drives are decrypted before mounting. -- _2026-05-03_ — Fixed CLI wrappers and removed obsolete code. (1) Updated `nomarchy-font`, `nomarchy-theme`, and `nomarchy-wallpaper` CLI wrappers to use modern Walker menus; (2) Removed the obsolete and broken `themes/engine/switcher.nix` and its associated Nix-inlined scripts; (3) Cleaned up remaining `$NOMARCHY_PATH` references from the Omarchy era. -- _2026-05-03_ — Fixed `/etc/nixos` ownership after installation. Added a `chown -R $USERNAME:users /etc/nixos` step via `nixos-enter` at the end of `installer/install.sh` so the main user owns their configuration and can run `home-manager` commands without `sudo`. -- _2026-05-01_ — Installer & Script Audit Polish. (1) Fixed a critical bash dynamic scoping bug in `installer/install.sh` where `rc=0` assignments inside functions (Impermanence, Form Factor) were clobbering the main loop's return code, causing the installer to abort when "No" was selected; (2) Polished `hosts/nomarchy-live.nix` with auto-login for the `nixos` user and passwordless sudo for the `wheel` group; (3) Repurposed `nomarchy-toggle-suspend` to execute `systemctl suspend` directly and updated `nomarchy-menu` to reflect this; (4) Updated `nomarchy-launch-wifi` to use `nmtui` in Alacritty; (5) Regenerated `docs/SCRIPTS.md` to reflect the updated script mappings. -- _2026-04-30_ — `set -e` sweep across `nomarchy-*` scripts. Added `set -e` to 142 of 169 bash scripts that lacked it (27 already had it). Halts a class of "command failed silently in the middle of a chain, system left in half-applied state" bugs that produced repeat-fix commits. One deliberate exception: `nomarchy-menu` runs without `set -e` because it's an interactive UX loop where action failures should re-display the menu rather than abort the script. Pre-commit hook now enforces `bash -n` + `shellcheck --severity=error` so future scripts can't regress this. -- _2026-04-30_ — Installer disk-phase reliability. Hardened `installer/install.sh` and consolidated the disko configs: (1) `select_disk` now hides the live-ISO boot device(s) so the installer can't format its own boot media (`NOMARCHY_INSTALL_ALLOW_ISO_TARGET=1` to override); (2) added a 10 GiB minimum-capacity preflight; (3) `prewipe_target_drive` enumerates every active dm-crypt mapping backed by the target drive and closes them, drops the silent `|| true` from `wipefs`/`sgdisk`/`dd`, bounds `udevadm settle` to 30s, and refuses to continue if anything is still mounted; (4) wrapped the disko call in `run_disko_with_retry` with last-30-lines + Retry / View full log / Abort dialog on failure; (5) replaced the sed-templated `disko-golden.nix` + `disko-btrfs-multi.nix` pair with a single `disko-config.nix` Nix function called via `--argstr mainDrive … --arg extraDrives '[…]'` — eliminates a class of escaping bugs (cf. `3aadc36`); (6) added an EXIT trap so the tmpfs LUKS key file is removed even on early abort. -- _2026-04-30_ — Gaming home-side companion. New `nomarchy.gaming.enable` option (mirror of `nomarchy.system.gaming.enable`) and `core/home/gaming.nix` module that injects a Hyprland `windowrulev2 = fullscreen, class:^(steam_app_).*$` so Steam-launched games grab the whole screen. Closes the "Gaming — Hyprland window rule" Next-column row. -- _2026-04-26_ — Default to highest resolution (`highres`) for monitors. Updated `features/desktop/hyprland/config/monitors.conf` and forced it in the live ISO (`nomarchy-live`) to resolve issues where some hardware would default to a low resolution (1024x768). -- _2026-04-26_ — First-run welcome wizard (`nomarchy-welcome`). Extended from a one-shot greeter into a guided picker for theme, font, and panel position. Added Step 4 to generate a starter `home.nix` if missing. State is now persisted in `state.json` via `.welcome_done`. Added `nomarchy.panelPosition` option to Waybar. -- _2026-04-26_ — Multi-disk BTRFS support in the installer. Added `installer/disko-btrfs-multi.nix` template and updated `installer/install.sh` to allow selecting multiple drives via `gum choose --no-limit`. Implements BTRFS "single" data + RAID1 metadata across multiple LUKS-encrypted drives. -- _2026-04-26_ — Distro Branding Phase 2. Updated bootloader entries to use "Nomarchy" as the label. Set ISO volume IDs to `NOMARCHY_INSTALLER` and `NOMARCHY_LIVE`. Fixed branding in Plymouth theme metadata and SDDM metadata. -- _2026-04-26_ — Distro Branding Phase 1. Renamed `installerIso` to `nomarchy-installer` and `installerIsoGraphical` to `nomarchy-live`. Updated metadata and host configurations. Scrubbed "Omarchy" from Plymouth and installer messages. -- _2026-04-26_ — Fix `hardware-db.sh` missing in `nomarchy-installer.nix`. Resolved boot error where `install.sh` failed to source the hardware database on the TTY installer ISO. -- _2026-04-26_ — Installer review-then-edit flow (`installer/install.sh`). Review screen now offers Continue / Edit a field / Abort. Edit opens a multi-select of saved fields; chosen fields clear and the next loop iteration re-prompts only those. Benefits both fresh installs (typo fixes without abort+restart) and `--resume` (lands on review immediately, since the loaded vars short-circuit each prompt). LUKS passphrase is held in memory across loop iterations so re-edits don't re-ask for it. -- _2026-04-26_ — `docs/TROUBLESHOOTING.md`. The five most common rebuild errors (option-already-declared, attribute-missing, Stylix target conflict, home-manager `.hm-bak` churn, impermanence path missing) with copy-paste fixes. Linked from `README.md` and `docs/MIGRATION.md`. -- _2026-04-26_ — Gaming preset module (`core/system/gaming.nix`). Opt-in `nomarchy.system.gaming.enable` (default false). Wires `programs.steam` (with `remotePlay`/`localNetworkGameTransfers` firewall holes via `mkDefault`), `programs.gamemode`, and `services.flatpak`. Flathub remote and Hyprland window-rule split into separate Next-column rows. -- _2026-04-26_ — Accessibility preset module (`core/system/accessibility.nix`). New `nomarchy.system.accessibility.{enable,cursorSize}` options (opt-in, default off — accessibility isn't a hardware-derived signal). Enables `services.gnome.at-spi2-core`, installs Orca, and sets `XCURSOR_SIZE=32` (configurable). Hyprland-side companion (key-repeat slowdown, Orca keybinding, high-contrast palette) split into a new Next-column row. -- _2026-04-26_ — Desktop preset module (`core/system/desktop.nix`). New `nomarchy.system.desktop.enable` option; defaults to `formFactor == "desktop"` (mirror of the laptop preset's auto-enable). Pins `powerManagement.cpuFreqGovernor` to `"performance"` and enables `services.zfs.{autoScrub,trim}` so a future ZFS pool gets sensible maintenance for free. -- _2026-04-26_ — Laptop preset module (`core/system/laptop.nix`). New `nomarchy.system.laptop.{enable,thermald}` options; `enable` defaults to `formFactor == "laptop"` so the installer's existing `formFactor` write auto-flips it on. Wires TLP (governors + 75/80 charge thresholds), force-disables `power-profiles-daemon`, enables `upower` and `thermald` (x86_64), adds the brightnessctl udev rule for backlight without root, and sets a logind lid-switch policy that defers to `hibernation.enable`. Closes both the Now item and the largest Next item. -- _2026-04-25_ — Software-profile multi-select in the installer. Users can now pick Dev, Gaming, Office, Media, and CLI Utils profiles during install; logic emits corresponding `home.packages` and system toggles into the generated config. -- _2026-04-25_ — Pillar 3 Phase B: script & menu audit. Ported/implemented/stubbed ~40 scripts including `nomarchy-version`, `nomarchy-debug`, `nomarchy-reinstall`, `nomarchy-rollback`, `nomarchy-update-firmware`, `nomarchy-pkg-*`, and `nomarchy-theme-*` wrappers. Moved desktop scripts to packaged utility directory. -- _2026-04-25_ — Docker & fwupd support. Added `nomarchy.system.virtualization.docker.enable` and `nomarchy.hardware.fwupd` options. Wires system services and adds `docker-compose` and `fwupdmgr` to PATH. -- _2026-04-25_ — Installer VM testing. Added `installerVm` to flake nixosConfigurations, packages, and apps. `nomarchy-test-installer` now uses `nix run .#installerVm`. -- _2026-04-25_ — `docs/KEYBINDINGS.md` auto-generator. New repo-tooling script `bin/utils/nomarchy-docs-keybindings` parses every `bindd =` / `bindeld =` line into a Markdown doc; README's keybinding table slimmed to highlights + link. -- _2026-04-25_ — Installer disk picker shows NAME / SIZE / TYPE / VENDOR / MODEL / SERIAL columns instead of bare `lsblk`. Type derived from `ROTA` + `TRAN` (NVMe / USB / SSD / HDD). Filters loop, ram, zram, sr. -- _2026-04-25_ — Pillar 3 Phase A: script & menu audit. New `bin/utils/nomarchy-docs-scripts` generator produces `docs/SCRIPTS.md` with 136 scripts and the menu walk pre-tagged via heuristics (`kept` / `unused?` / `missing`). Phase B (per-batch porting / removal) opens. -- _2026-04-25_ — Installer prompts for keyboard layout + locale, applies live; new `nomarchy.{system,}.formFactor` option; waybar drops battery widget on desktop; nm-applet visibility fix in default theme; live-ISO baseline keymap/locale (`a7e7fa9`). -- _2026-04-25_ — `docs/OPTIONS.md` reference; `docs/MIGRATION.md` linked from `README.md` (`3cb012b`, `6ef28f0`). diff --git a/docs/SCRIPTS.md b/docs/SCRIPTS.md deleted file mode 100644 index 45d76d6..0000000 --- a/docs/SCRIPTS.md +++ /dev/null @@ -1,263 +0,0 @@ -# Nomarchy Script & Menu Audit - -Auto-generated table for [Pillar 3 of the roadmap](ROADMAP.md#3-pillar-script--menu-audit). -**Do not edit by hand.** Regenerate after script or menu changes: - -```bash -./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md -``` - -The status column uses a Phase A heuristic — `kept` / `unused?` / `missing`. -Phase B (per-batch PRs) refines those into `port-from-omarchy`, -`delete-dead`, or `stub-with-notify` and updates the rows. - -## Status legend - -- `kept` — script exists and is called from somewhere outside its own directory. -- `unused?` — script exists but no caller was found. Could be dead, could be - intentional public API. Phase B triage decides. -- `missing` — referenced from code but no script file exists. Phase B triage - decides whether to port from Omarchy upstream, delete the caller, or stub - with `notify-send`. -- `port-from-omarchy` — Phase B verdict: lift the upstream Omarchy script, - rewrite for NixOS paths. -- `delete-dead` — Phase B verdict: remove and update callers. -- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub. - -## Scripts (151) - -| Script | Location | Callers | Status | Notes | -| --- | --- | --- | --- | --- | -| `nomarchy-backup` | `features/scripts/utils` | features/scripts/utils/nomarchy-sync | `kept` | | -| `nomarchy-battery-capacity` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | | -| `nomarchy-battery-monitor` | `core/system/scripts` | features/scripts/battery-monitor.nix | `kept` | | -| `nomarchy-battery-remaining` | `core/system/scripts` | core/system/scripts/nomarchy-battery-monitor,core/system/scripts/nomarchy-battery-status | `kept` | | -| `nomarchy-battery-remaining-time` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | | -| `nomarchy-battery-status` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/system/scripts/nomarchy-battery-capacity, +1 more | `kept` | | -| `nomarchy-brightness-display` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | | -| `nomarchy-brightness-display-apple` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | | -| `nomarchy-brightness-keyboard` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | | -| `nomarchy-build-iso` | `features/scripts/utils` | README.md | `kept` | | -| `nomarchy-build-live-iso` | `features/scripts/utils` | README.md | `kept` | | -| `nomarchy-cmd-audio-switch` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | | -| `nomarchy-cmd-present` | `features/scripts/utils` | core/home/config/nomarchy/hooks/battery-low.sample,features/scripts/utils/nomarchy-launch-editor, +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` | | -| `nomarchy-cmd-share` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-cmd-terminal-cwd` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf | `kept` | | -| `nomarchy-debug` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-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/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` | | -| `nomarchy-font-set` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-font, +4 more | `kept` | | -| `nomarchy-haptic-touchpad` | `core/system/scripts` | core/system/hardware.nix,core/system/scripts-derivation.nix | `kept` | | -| `nomarchy-hibernation-available` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-hibernation-remove` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-hibernation-setup` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-hook` | `features/scripts/utils` | core/system/scripts/nomarchy-battery-monitor,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` | — | `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` | | -| `nomarchy-hyprland-window-gaps-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-hyprland-window-pop` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf | `kept` | | -| `nomarchy-hyprland-window-single-square-aspect-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-hyprland-workspace-layout-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-install` | `features/scripts/utils` | README.md,core/home/config/nomarchy-skill/SKILL.md, +2 more | `kept` | | -| `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, +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 | `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, +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, +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,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-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` | | -| `nomarchy-refresh-fastfetch` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-refresh-hyprland` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-refresh-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-reinstall` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-restart-app` | `features/scripts/utils` | core/system/scripts/nomarchy-restart-xcompose,features/scripts/utils/nomarchy-restart-hypridle, +3 more | `kept` | | -| `nomarchy-restart-bluetooth` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-restart-btop` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | | -| `nomarchy-restart-hypridle` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-restart-hyprsunset` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-restart-opencode` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | | -| `nomarchy-restart-pipewire` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-restart-swayosd` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-restart-terminal` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-restart-trackpad` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-restart-walker` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +1 more | `kept` | | -| `nomarchy-restart-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +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` | | -| `nomarchy-setup-fido2` | `core/system/scripts` | features/scripts/utils/nomarchy-menu,installer/install.sh | `kept` | | -| `nomarchy-setup-fingerprint` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-show-done` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | | -| `nomarchy-show-logo` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | | -| `nomarchy-skill` | `features/scripts/utils` | core/home/configs.nix | `kept` | | -| `nomarchy-state` | `features/scripts/utils` | core/system/scripts/nomarchy-system-reboot,core/system/scripts/nomarchy-system-shutdown, +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, +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` | | -| `nomarchy-test-installer` | `features/scripts/utils` | README.md,features/scripts/utils/nomarchy-test-vm | `kept` | | -| `nomarchy-test-live-iso` | `features/scripts/utils` | hosts/nomarchy-live.nix | `kept` | | -| `nomarchy-test-vm` | `features/scripts/utils` | features/scripts/utils/nomarchy-test-live-iso | `kept` | | -| `nomarchy-theme` | `features/scripts/utils` | README.md,bin/utils/nomarchy-docs-scripts, +17 more | `kept` | | -| `nomarchy-theme-bg-install` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-theme-bg-next` | `themes/engine/scripts` | README.md,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | | -| `nomarchy-theme-bg-set` | `themes/engine/scripts` | features/apps/elephant/config/menus/nomarchy_background_selector.lua,features/scripts/utils/nomarchy-wallpaper | `kept` | | -| `nomarchy-theme-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,themes/engine/scripts/nomarchy-theme-next | `kept` | | -| `nomarchy-theme-install` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-theme-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-theme, +2 more | `kept` | | -| `nomarchy-theme-next` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-theme-refresh` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-theme-remove` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-theme-set` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/apps/elephant/config/menus/nomarchy_themes.lua, +9 more | `kept` | | -| `nomarchy-theme-set-keyboard` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | | -| `nomarchy-theme-set-keyboard-asus-rog` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | | -| `nomarchy-theme-set-keyboard-f16` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | | -| `nomarchy-theme-set-obsidian` | `themes/engine/scripts` | themes/engine/scripts/nomarchy-theme-set | `kept` | | -| `nomarchy-theme-set-templates` | `themes/engine/scripts` | themes/engine/scripts/nomarchy-theme-set | `kept` | | -| `nomarchy-theme-update` | `themes/engine/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-themes-prebuild` | `themes/engine/scripts` | installer/install.sh | `kept` | | -| `nomarchy-toggle-hybrid-gpu` | `core/system/scripts` | features/scripts/utils/nomarchy-menu,features/scripts/utils/nomarchy-sys-update, +1 more | `kept` | | -| `nomarchy-toggle-idle` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +2 more | `kept` | | -| `nomarchy-toggle-nightlight` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +2 more | `kept` | | -| `nomarchy-toggle-notification-silencing` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | | -| `nomarchy-toggle-screensaver` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-toggle-suspend` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | -| `nomarchy-toggle-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +1 more | `kept` | | -| `nomarchy-tui-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-tui-remove-all | `kept` | | -| `nomarchy-tui-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-tui-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | -| `nomarchy-tz-select` | `core/system/scripts` | features/desktop/waybar/config/config.jsonc,features/scripts/utils/nomarchy-menu, +2 more | `kept` | | -| `nomarchy-update` | `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-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,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` | | - -## Missing references - -Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bin/`, `lib/` that have no matching script file. - -| Token | Referenced in | Status | -| --- | --- | --- | - -## Menu items - -Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*_menu` function becomes one row. - -| Submenu | Entry | Calls | Status | -| --- | --- | --- | --- | -| `show_learn_menu` | Keybindings | `nomarchy-menu-keybindings` | `kept` | -| `show_learn_menu` | Nomarchy | `nomarchy-manual` | `kept` | -| `show_learn_menu` | Hyprland | `nomarchy-launch-webapp` | `kept` | -| `show_learn_menu` | Arch | `nomarchy-launch-webapp` | `kept` | -| `show_learn_menu` | Bash | `nomarchy-launch-webapp` | `kept` | -| `show_learn_menu` | Neovim | `nomarchy-launch-webapp` | `kept` | -| `show_trigger_menu` | Capture | `_(inline)_` | `kept` | -| `show_trigger_menu` | Share | `_(inline)_` | `kept` | -| `show_trigger_menu` | Toggle | `_(inline)_` | `kept` | -| `show_trigger_menu` | Hardware | `_(inline)_` | `kept` | -| `show_capture_menu` | Screenshot | `nomarchy-cmd-screenshot` | `kept` | -| `show_capture_menu` | Screenrecord | `_(inline)_` | `kept` | -| `show_capture_menu` | Color | `_(inline)_` | `kept` | -| `show_share_menu` | Clipboard | `nomarchy-cmd-share` | `kept` | -| `show_share_menu` | File | `nomarchy-cmd-share` | `kept` | -| `show_share_menu` | Folder | `nomarchy-cmd-share` | `kept` | -| `show_toggle_menu` | Screensaver | `nomarchy-toggle-screensaver` | `kept` | -| `show_toggle_menu` | Nightlight | `nomarchy-toggle-nightlight` | `kept` | -| `show_toggle_menu` | Idle | `nomarchy-toggle-idle` | `kept` | -| `show_toggle_menu` | Bar | `nomarchy-toggle-waybar` | `kept` | -| `show_toggle_menu` | Layout | `nomarchy-hyprland-workspace-layout-toggle` | `kept` | -| `show_toggle_menu` | Ratio | `nomarchy-hyprland-window-single-square-aspect-toggle` | `kept` | -| `show_toggle_menu` | Gaps | `nomarchy-hyprland-window-gaps-toggle` | `kept` | -| `show_toggle_menu` | Scaling | `nomarchy-hyprland-monitor-scaling-cycle` | `kept` | -| `show_style_menu` | Theme | `_(inline)_` | `kept` | -| `show_style_menu` | Font | `_(inline)_` | `kept` | -| `show_style_menu` | Background | `_(inline)_` | `kept` | -| `show_style_menu` | Hyprland | `_(inline)_` | `kept` | -| `show_style_menu` | Screensaver | `_(inline)_` | `kept` | -| `show_style_menu` | About | `_(inline)_` | `kept` | -| `show_setup_menu` | Audio | `nomarchy-launch-audio` | `kept` | -| `show_setup_menu` | Wifi | `nomarchy-launch-wifi` | `kept` | -| `show_setup_menu` | Bluetooth | `nomarchy-launch-bluetooth` | `kept` | -| `show_setup_menu` | Power | `_(inline)_` | `kept` | -| `show_setup_menu` | System | `_(inline)_` | `kept` | -| `show_setup_menu` | Monitors | `_(inline)_` | `kept` | -| `show_setup_menu` | Keybindings | `_(inline)_` | `kept` | -| `show_setup_menu` | Input | `_(inline)_` | `kept` | -| `show_setup_menu` | DNS | `nomarchy-setup-dns` | `kept` | -| `show_setup_menu` | Security | `_(inline)_` | `kept` | -| `show_setup_security_menu` | Fingerprint | `nomarchy-setup-fingerprint` | `kept` | -| `show_setup_security_menu` | Fido2 | `nomarchy-setup-fido2` | `kept` | -| `show_update_menu` | Nomarchy | `nomarchy-update` | `kept` | -| `show_update_menu` | Themes | `nomarchy-theme-update` | `kept` | -| `show_update_menu` | Process | `_(inline)_` | `kept` | -| `show_update_menu` | Hardware | `_(inline)_` | `kept` | -| `show_update_menu` | Firmware | `nomarchy-update-firmware` | `kept` | -| `show_update_menu` | Timezone | `nomarchy-tz-select` | `kept` | -| `show_update_menu` | Time | `nomarchy-update-time` | `kept` | -| `show_update_menu` | Password | `_(inline)_` | `kept` | -| `show_update_process_menu` | Hypridle | `nomarchy-restart-hypridle` | `kept` | -| `show_update_process_menu` | Hyprsunset | `nomarchy-restart-hyprsunset` | `kept` | -| `show_update_process_menu` | Swayosd | `nomarchy-restart-swayosd` | `kept` | -| `show_update_process_menu` | Walker | `nomarchy-restart-walker` | `kept` | -| `show_update_process_menu` | Waybar | `nomarchy-restart-waybar` | `kept` | -| `show_update_hardware_menu` | Audio | `nomarchy-restart-pipewire` | `kept` | -| `show_update_hardware_menu` | Wi-Fi | `nomarchy-restart-wifi` | `kept` | -| `show_update_hardware_menu` | Bluetooth | `nomarchy-restart-bluetooth` | `kept` | -| `show_update_password_menu` | Drive | `nomarchy-drive-set-password` | `kept` | -| `show_update_password_menu` | User | `_(inline)_` | `kept` | -| `show_system_menu` | Screensaver | `nomarchy-launch-screensaver` | `kept` | -| `show_system_menu` | Lock | `nomarchy-lock-screen` | `kept` | -| `show_system_menu` | Suspend | `nomarchy-toggle-suspend` | `kept` | -| `show_system_menu` | Hibernate | `_(inline)_` | `kept` | -| `show_system_menu` | Logout | `nomarchy-system-logout` | `kept` | -| `show_system_menu` | Restart | `nomarchy-system-reboot` | `kept` | -| `show_system_menu` | Shutdown | `nomarchy-system-shutdown` | `kept` | - diff --git a/docs/STRUCTURE.md b/docs/STRUCTURE.md deleted file mode 100644 index 8df93de..0000000 --- a/docs/STRUCTURE.md +++ /dev/null @@ -1,143 +0,0 @@ -# Nomarchy - Architecture & Directory Structure - -Nomarchy is a NixOS-based distribution characterized by its **Modular Merging Architecture**. This design strictly separates the "Upstream" core (the distro's logic) from the "Downstream" configuration (the user's personal setup), while allowing for dynamic, state-based theming and a highly modular desktop environment. - -## Table of Contents -1. [Core Principles](#core-principles) -2. [Root Directory](#root-directory) -3. [The `core/` Directory (Foundational)](#the-core-directory-foundational) -4. [The `features/` Directory (Apps & Desktop)](#the-features-directory-apps--desktop) -5. [The `themes/` Directory (Dynamic Theming)](#the-themes-directory-dynamic-theming) -6. [The `lib/` Directory (Shared Library)](#the-lib-directory-shared-library) -7. [The `installer/` & `hosts/` Directories (Deployment)](#the-installer--hosts-directories-deployment) - ---- - -## Core Principles - -### Upstream vs. Downstream -- **Upstream:** The code in this repository is treated as the "Upstream" source. It provides the base OS configurations, dynamic theming engine, and modular application logic. -- **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 -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. - ---- - -## Root Directory - -- **`flake.nix`**: The master entry point for the entire distribution. - - **Inputs:** Defines external dependencies like `nixpkgs`, `home-manager`, `disko`, `stylix`, and others. - - **Outputs:** - - `nixosModules.system`: Exports the foundational OS logic (`./core`). - - `nixosModules.home`: Exports the application and desktop logic (`./features`). - - `nixosConfigurations`: Defines pre-configured targets like `nomarchy-installer`, `nomarchy-live`, and a testing `vm`. -- **`flake.lock`**: Locks dependency versions for reproducible builds. -- **`README.md`**: Project overview, installation instructions, and basic usage. -- **`docs/`**: All long-form documentation. Key entry points: - - **`AGENT.md`**: Onboarding for AI coding agents picking up Nomarchy. - - **`STRUCTURE.md`**: (This file) Detailed architectural documentation. - - **`OPTIONS.md`**: Reference for every `nomarchy.*` option. - - **`ROADMAP.md`**: Now / Next / Later board and the Shipped log. - - **`MIGRATION.md`**: Layering Nomarchy onto an existing NixOS install. - - **`KEYBINDINGS.md`**: Auto-generated keybinding reference. - - **`SCRIPTS.md`**: Auto-generated `nomarchy-*` script audit. - - **`TROUBLESHOOTING.md`**: Common rebuild errors and fixes. - - **`creating-themes.md`**: Theme palette authoring guide. -- **`.gitea/workflows/`**: Gitea/Forgejo Actions CI (the `.gitea/` path is scanned by both Gitea and Forgejo; `.forgejo/` is *not* scanned by Gitea). Runs `nix flake check --no-build`, the opt-in toggle eval matrix (`bin/utils/nomarchy-eval-matrix`), lints every `nomarchy-*` bash script with `bash -n` + `shellcheck --severity=error`, and verifies `docs/SCRIPTS.md` is up to date on every push to `main` and every PR. To activate: enable Actions on the repo and register an `act_runner` (any Docker-capable Linux host works — including the Gitea server itself; the workflow uses `ubuntu-latest` and installs Nix itself). -- **`.githooks/`**: Optional per-clone git hooks (`pre-commit` lints changed scripts and regenerates `docs/SCRIPTS.md`). Enable with `git config core.hooksPath .githooks`. CI enforces the same invariants tree-wide. - ---- - -## The `core/` Directory (Foundational) - -The `core/` directory contains the foundational modules required for a functional system and user environment. - -### `core/system/` (OS-Level) -- **`default.nix`**: The central entry point for the system module, importing all OS components. -- **`options.nix`**: Defines the `nomarchy.system` configuration options (e.g., DNS, Timezone, Feature toggles). -- **`state.nix`**: Loads and applies the system-level state (from `/etc/nixos/state.json`). -- **`audio.nix`**: Configures Pipewire, Wireplumber, and audio-related settings. -- **`bluetooth.nix`**: Bluetooth stack and management tools. -- **`browser.nix`**: System-level browser configurations and protocols. -- **`network.nix`**: NetworkManager configuration, DNS optimization, and Wi-Fi powersave settings. -- **`hardware.nix`**: Generic hardware support and hardware-specific script triggers. -- **`virtualization.nix`**: Libvirtd, Docker, and VM guest support. -- **`impermanence.nix`**: Root-on-RAM/Impermanence setup for ephemeral root filesystems. -- **`scripts/`**: A collection of low-level system scripts (e.g., `nomarchy-hw-match`, `nomarchy-setup-dns`). - -### `core/home/` (User-Level) -- **`default.nix`**: The entry point for the base Home Manager module. -- **`options.nix`**: Defines the `nomarchy` user options (Toggles, Theme, Fonts, etc.). -- **`state.nix`**: Loads and applies user-level state (from `~/.config/nomarchy/state.json`). -- **`overrides.nix`**: Declares `nomarchy.overrides.*` (reserved for a future file-based override loader; currently no-op). -- **`configs.nix`**: Manages static configuration files and directories in `~/.config/`. Honors `nomarchy.configOverrides` as a bulk redirect to a replacement config dir. -- **`bash.nix`**: Shell environment, aliases, and specialized `env-update` hooks. -- **`security.nix`**: Polkit, keyring management, and GPG settings. -- **`config/`**: Contains the physical source files for the base user configuration (e.g., `starship.toml`, `hypr/` behavior configs). - ---- - -## The `features/` Directory (Apps & Desktop) - -The `features/` directory contains optional, modular components that define the user's interactive environment. - -- **`default.nix`**: Aggregates and enables all sub-modules in `features/`. -- **`apps/`**: Individual application modules. - - Each app (e.g., `alacritty`, `btop`, `vscode`, `ghostty`) has its own directory containing a `default.nix` and a `config/` subdirectory. - - Apps are configured using the "Individual File Management" pattern to avoid directory symlink conflicts with the theme loader. -- **`desktop/`**: The graphical environment core. - - **`hyprland/`**: The primary tiling window manager configuration. - - **`waybar/`**: The status bar configuration, including dynamic CSS generation. - - **`idle.nix`** & **`nightlight.nix`**: Management of screen timeouts and blue light filters. -- **`scripts/`**: High-level utility scripts (e.g., `nomarchy-update`, `nomarchy-theme-set`). - - **`utils/`**: Helper scripts like `nomarchy-launch-or-focus-tui` or `nomarchy-restart-*`. - ---- - -## The `themes/` Directory (Dynamic Theming) - -The theming system is the "soul" of Nomarchy, providing dynamic, consistent aesthetics across all applications. - -### `themes/engine/` (The Logic) -- **`loader.nix`**: The core theme loader. It reads the active theme from state and selectively deploys app-specific themed configs (e.g., `btop.theme`, `kitty.conf`) to `~/.config/`. -- **`stylix.nix`**: Integration with Stylix for base color palette and wallpaper management. -- **`plymouth.nix`** & **`sddm.nix`**: System-level theming for the boot screen and login manager. - -### `themes/palettes/` (The Data) -- Contains subdirectories for every available theme (e.g., `summer-night`, `nord`, `tokyo-night`). -- Each theme directory includes: - - `colors.toml`: The Base16 color definition. - - `backgrounds/`: A collection of theme-aware wallpapers. - - `apps/`: Themed overrides for specific applications (e.g., `btop.theme`, `vscode.json`). - -### `themes/templates/` (The Blueprints) -- Contains `.tpl` files (e.g., `waybar.css.tpl`, `hyprland.conf.tpl`) used to generate dynamic configuration files that incorporate the current theme's color palette. - ---- - -## The `lib/` Directory (Shared Library) - -The `lib/` directory provides centralized logic and data structures to maintain consistency. - -- **`default.nix`**: A shared Nix library providing helper functions: - - `readState`: Safely reads JSON/text state files. - - `getPalette` / `getColorScheme`: Resolves theme names to color data. - - `resolveWallpaper`: Logic for choosing the correct background image. - - `getIconsTheme`: Resolves the appropriate icon set for a theme. -- **`state-schema.nix`**: Defines the single source of truth for the shape and default values of the Nomarchy state (both system and home). - ---- - -## The `installer/` & `hosts/` Directories (Deployment) - -### `installer/` (Bootstrap) -- **`install.sh`**: The interactive TTY-based installer. It handles disk partitioning, NixOS installation, and generating a clean "Downstream" flake for the user. -- **`disko-config.nix`**: The disko partition layout (BTRFS on top of LUKS2). A Nix function of `{ mainDrive, extraDrives ? [] }` — single-disk path is `extraDrives = []`; multi-disk adds BTRFS `-d single -m raid1` across the extras. Invoked by `install.sh` via `disko --argstr mainDrive … --arg extraDrives '[…]'`. - -### `hosts/` (Targets) -- **`nomarchy-installer.nix`**: Configuration for the minimal, TTY-based installation ISO. -- **`nomarchy-live.nix`**: Configuration for the full graphical live environment, used for testing and GUI-based installation. diff --git a/docs/TESTING.md b/docs/TESTING.md new file mode 100644 index 0000000..b9bc829 --- /dev/null +++ b/docs/TESTING.md @@ -0,0 +1,117 @@ +# Testing Nomarchy + +How to verify changes actually work — from cheap eval checks to booting the +full desktop. Adapted from the previous Nomarchy iteration's agent docs. + +**The honesty rule (for humans and AI agents alike):** for Waybar, Hyprland, +or any visual change, the only reliable check is booting the live ISO. If +you can't boot it (e.g. you're on macOS or have no KVM), **say so** rather +than claiming success. "All Nix files parse" is not "the bar renders." + +## 1. Cheap checks (always do these first) + +```sh +nix flake check --no-build # full module-system evaluation, no builds +nix-instantiate --parse <file> # syntax-only, works even on macOS +python3 -m py_compile pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py +``` + +`nix flake check` needs a Linux machine (or a Linux builder) since all +outputs are `x86_64-linux`. It catches type errors, missing options, and +bad merges — most breakage stops here. It also evaluates the downstream +template through `lib.mkFlake` (including a real nixos-hardware profile), +so template/wrapper drift fails fast too. + +## 2. Build and boot the live ISO + +```sh +# Build + boot in QEMU, one command: +tools/test-live-iso.sh + +# Or by hand: +nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage +# → result/iso/*.iso — dd it to a USB stick for real hardware +``` + +The script prefers UEFI (OVMF) with a legacy-BIOS fallback, uses KVM when +`/dev/kvm` is readable, and boots with `virtio-vga-gl` + `gl=on` — +**Hyprland and Ghostty need real OpenGL in the guest**; without it the +session may not start. + +### What the live environment gives you + +- Auto-login as `nomarchy` (no password, passwordless sudo), greetd → + Hyprland via `initial_session`; logging out lands on tuigreet. +- The flake is seeded writable at `~/.nomarchy` (from the read-only + `/etc/nomarchy` copy) — `$NOMARCHY_PATH` already points there. +- Locked flake inputs are pinned into the ISO store, so + `home-manager switch` works **without a network**. + +## 3. Verification checklist + +Work through these in order; each one exercises a different layer. + +| # | Check | Verifies | +|---|---|---| +| 1 | Boots to Hyprland with Tokyo Night wallpaper visible | greetd autologin, awww, session start | +| 2 | Waybar shows at top, themed (blue accent on dark) | HM waybar module, palette baking | +| 3 | `SUPER+Return` opens Ghostty with Tokyo Night colors | terminal default, ANSI palette | +| 4 | `btop` in the terminal is themed | per-theme asset baking | +| 5 | `nomarchy-theme-sync list` prints 21 presets | package, baked themes dir | +| 6 | `nomarchy-theme-sync apply gruvbox` → state written, `home-manager switch` runs, desktop re-themes, wallpaper changes | the whole engine: state write, pure eval, HM rebuild, wallpaper hook | +| 7 | `SUPER+SHIFT+T` cycles wallpapers instantly (try `tokyo-night`: 4 of them) | the runtime wallpaper path | +| 8 | `nomarchy-theme-sync apply summer-night` → after the switch the bar has its own identity (light bar, different styling) | whole-swap waybar.css assets | +| 9 | Open a GTK app — dark theme matching the palette | Stylix layer | +| 10 | `home-manager generations` lists one generation per theme change; activating an older one rolls the theme back | atomicity / rollback story | + +Items 6 and 8 are the big ones — they prove the all-Home-Manager theming +model end to end. + +## 4. Testing the installer + +One command runs the whole offline regression: + +```sh +tools/test-install.sh +``` + +It builds the ISO, boots it in QEMU **with networking disabled**, runs an +unattended LUKS+swap install onto a blank disk via the installer's +`NOMARCHY_UNATTENDED=1` mode (config delivered on a small vfat disk — +typing long commands into a guest drops keystrokes), waits for the +installer's poweroff, then boots the installed disk, enters the +passphrase, and screenshots the first boot. The machine-checkable parts +(install completes, disk boots) fail the script; the visual verdict — +**themed desktop, no autogenerated-config banner, no login prompt** — is +yours, from `first-boot.png`. The helpers it builds on live in +`tools/vm/` (QMP key injection, GL-safe VNC screenshots) and work for any +manual VM poking; `tools/vm/gap-analysis.py` is the maintainer tool for +diagnosing offline installs that try to build from source. + +To test by hand instead, replicate what `tools/test-install.sh` does: the +unattended env it uses is in the script, and the same flow works +interactively from the live terminal. If the desktop comes up unthemed, +read `/var/log/nomarchy-hm-preactivate.log` on the installed system. + +## 5. VM-specific gotchas + +- **No KVM** (e.g. nested without acceleration): everything works but + `home-manager switch` will be painfully slow. Don't read slowness as + breakage. +- **Black screen / session exits**: almost always missing guest GL. Use the + script's qemu flags; on real hardware check `journalctl -b -u greetd`. +- **Tiny resolution**: the live config forces `monitor = ,highres,auto,1`; + if QEMU still picks 1024×768, resize the window — Hyprland follows. +- **First theme switch is the slowest**: it evaluates the flake on a RAM + disk. Subsequent switches reuse the eval cache. + +## 6. When something fails + +- Session/login problems: `journalctl -b -u greetd`, then `journalctl + --user -b`. +- Theme switch failures: run `nomarchy-theme-sync apply <x>` from a + terminal — the home-manager output streams there. The state file is + written *before* the rebuild, so after fixing you can just re-run + `home-manager switch --flake ~/.nomarchy`. +- Report results faithfully: what booted, what rendered, what you could + not verify and why. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md deleted file mode 100644 index d2d3ea3..0000000 --- a/docs/TROUBLESHOOTING.md +++ /dev/null @@ -1,121 +0,0 @@ -# Troubleshooting - -The five rebuild errors a Nomarchy user is most likely to hit, with copy-paste fixes. If your error isn't here, the [Options Reference](OPTIONS.md) and [Migration Guide](MIGRATION.md) cover most other surfaces. - ---- - -## 1. `error: The option 'X' is already declared in multiple modules` - -**Looks like:** - -``` -error: The option `services.foo.enable' is already declared in multiple modules: - - /nix/store/…-source/modules/foo.nix - - /nix/store/…-source/core/system/foo.nix -``` - -**Cause:** Two modules — usually one of yours and one of Nomarchy's — both `mkOption` the same path. Nix can't merge two declarations of the *same option*; it can only merge two *values* for one option. - -**Fix:** Find the duplicate. If it's yours and Nomarchy's, delete yours; Nomarchy already declares it. If it's two of yours, delete one. To grep: - -```bash -grep -rn "options\..*foo\.enable" /etc/nixos -``` - ---- - -## 2. `error: attribute 'X' missing` - -**Looks like:** - -``` -error: attribute 'nomarchy' missing - at /etc/nixos/system.nix:7:5: - 7| nomarchy.system.formFactor = "laptop"; -``` - -**Cause:** You're setting `nomarchy.*` options but didn't import the Nomarchy modules. The flake's `nixosModules.system` declares the option namespace; without the import, the option doesn't exist. - -**Fix:** In your `/etc/nixos/flake.nix`, make sure both modules are in the system's `modules = [ … ]` list: - -```nix -modules = [ - nomarchy.nixosModules.system - ./system.nix - ./hardware-selection.nix -]; -``` - -The home side is the same shape — `nomarchy.nixosModules.home` plus your `./home.nix`. See [MIGRATION.md](MIGRATION.md) for the full skeleton. - ---- - -## 3. Stylix target conflict - -**Looks like:** - -``` -error: The option `stylix.targets.gtk.enable' has conflicting definition values: - - In `/nix/store/…/stylix.nix': true - - In `/etc/nixos/home.nix': false -Use `lib.mkForce' or `lib.mkDefault' to resolve. -``` - -**Cause:** Stylix's per-target options are `bool`, not `enum`, so two equally-priority `true`/`false` values from two modules collide. Nomarchy's theming engine sets most targets to `true` via `mkDefault`, so the conflict almost always means *you* set one without `mkDefault`. - -**Fix:** Wrap your override in `lib.mkForce`: - -```nix -stylix.targets.gtk.enable = lib.mkForce false; -``` - -If you want the default-on behavior back, just delete your line — Nomarchy's default fires automatically. - ---- - -## 4. home-manager `backupFileExtension` churn - -**Looks like:** every rebuild leaves another `~/.config/foo/bar.conf.hm-bak` next to the file home-manager just wrote, until your `~/.config` is half backups. - -**Cause:** home-manager refuses to overwrite files it didn't itself write; it backs them up first. Nomarchy's flake sets `backupFileExtension = "hm-bak"` (see `flake.nix:161,210`) so the first rebuild after a fresh ISO install doesn't fail — but every subsequent rebuild then re-backs-up the same files because the previous backup is still there. - -**Fix:** After the first successful rebuild, delete the backups: - -```bash -find ~/.config -name '*.hm-bak' -print -delete -``` - -If churn continues, you have a config under `~/.config/<app>/` that home-manager wants to manage but you've also touched by hand. Either let home-manager own it (don't edit by hand; use `nomarchy.*` options or `~/.config/nomarchy/overrides/`) or delete the home-manager declaration if you want the file to remain user-mutable. - ---- - -## 5. impermanence path missing after a wipe - -**Looks like:** after enabling `nomarchy.system.impermanence.enable = true;` and rebooting, an app forgets state — Bluetooth pairings vanish, NetworkManager forgets Wi-Fi, GPG keys are gone — or the rebuild itself errors with: - -``` -error: The path '/persist' does not exist -``` - -**Cause:** Impermanence requires (a) a `/persist` mountpoint that survives the boot wipe, and (b) every directory you want to keep must be in the persistence list. Nomarchy persists the basics in `core/system/impermanence.nix:91-120` (NetworkManager, Bluetooth, fprint, SSH host keys, the user's `.ssh` / `.gnupg` / Documents / Downloads / Pictures / Videos / Projects). Anything else you care about — Steam library, Flatpak data, custom dotfiles — must be added. - -**Fix:** Make sure `/persist` is mounted (check `mount | grep persist`). Then add the missing path in your `system.nix`: - -```nix -environment.persistence."/persist".users.nomarchy.directories = [ - ".local/share/Steam" - ".var/app" # flatpak data - ".local/share/keyrings" # already in Nomarchy defaults — example only -]; -``` - -Per-app data lives in `~/.local/share/<app>` or `~/.var/app/<id>` (flatpak); check the app's docs. After adding, rebuild and reboot — the path is created on the next mount of `/persist`. - ---- - -## Where to look next - -- **Option reference:** [docs/OPTIONS.md](OPTIONS.md) — every `nomarchy.*` setting. -- **Existing-NixOS install:** [docs/MIGRATION.md](MIGRATION.md) — how to layer Nomarchy onto a working NixOS without reformatting. -- **Repo layout:** [docs/STRUCTURE.md](STRUCTURE.md) — where each module lives. -- **Roadmap:** [docs/ROADMAP.md](ROADMAP.md) — what's planned and what's shipped. diff --git a/docs/WORKING-ON-NOMARCHY.md b/docs/WORKING-ON-NOMARCHY.md deleted file mode 100644 index 8865d80..0000000 --- a/docs/WORKING-ON-NOMARCHY.md +++ /dev/null @@ -1,210 +0,0 @@ -# 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. diff --git a/docs/creating-themes.md b/docs/creating-themes.md deleted file mode 100644 index 17af5d3..0000000 --- a/docs/creating-themes.md +++ /dev/null @@ -1,239 +0,0 @@ -# Creating a Nomarchy Theme - -This guide walks through creating a new theme for Nomarchy from scratch. - -## Theme Directory Structure - -Each theme lives in `themes/palettes/<theme-name>/` with the following structure: - -``` -themes/palettes/my-theme/ -├── colors.toml # REQUIRED - Color palette definition -├── icons.theme # REQUIRED - GTK icon theme name (single line) -├── light.mode # Optional - Empty marker file for light themes -├── preview.png # Optional - Preview image for the theme selector -├── backgrounds/ # Optional - Wallpaper images -│ ├── 1-primary.jpg # Numbered prefix controls default order -│ ├── 2-alternate.png -│ └── ... -└── apps/ # Optional - App-specific themed configs - ├── btop.theme # btop color theme - ├── neovim.lua # Neovim colorscheme plugin spec - ├── vscode.json # VS Code theme extension reference - └── hyprland.conf # Hyprland visual overrides (colors, decorations, animations) -``` - -## Step 1: Create the Color Palette - -Create `colors.toml` with all 24 required color fields. Every color must be a hex value with a `#` prefix. - -```toml -# UI colors -accent = "#83b6af" -cursor = "#d3c6aa" -foreground = "#d3c6aa" -background = "#2d353b" -selection_foreground = "#d3c6aa" -selection_background = "#505a60" - -# ANSI 16-color palette (color0-15) -# Normal colors -color0 = "#3c474d" # Black -color1 = "#e68183" # Red -color2 = "#a7c080" # Green -color3 = "#d9bb80" # Yellow -color4 = "#83b6af" # Blue -color5 = "#d39bb6" # Magenta -color6 = "#87c095" # Cyan -color7 = "#868d80" # White - -# Bright colors -color8 = "#868d80" # Bright Black -color9 = "#e68183" # Bright Red -color10 = "#a7c080" # Bright Green -color11 = "#d9bb80" # Bright Yellow -color12 = "#83b6af" # Bright Blue -color13 = "#d39bb6" # Bright Magenta -color14 = "#87c095" # Bright Cyan -color15 = "#868d80" # Bright White -``` - -These colors are mapped to a Base16 palette automatically: - -| Base16 Slot | Source Field | Typical Role | -|-------------|---------------|----------------------------| -| base00 | background | Default background | -| base01 | color0 | Lighter background | -| base02 | color8 | Selection background | -| base03 | color8 | Comments, invisibles | -| base04 | color7 | Dark foreground | -| base05 | foreground | Default foreground | -| base06 | color15 | Light foreground | -| base07 | color15 | Lightest foreground | -| base08 | color1 | Red (errors, deletions) | -| base09 | color3 | Orange | -| base0A | color3 | Yellow (warnings) | -| base0B | color2 | Green (success, additions) | -| base0C | color6 | Cyan (info) | -| base0D | color4 | Blue (primary accent) | -| base0E | color5 | Magenta (keywords) | -| base0F | color1 | Brown (deprecated) | - -## Step 2: Set the Icon Theme - -Create `icons.theme` with a single line containing the GTK icon theme name: - -``` -Yaru-blue -``` - -Common options from the `yaru-theme` package: `Yaru-blue`, `Yaru-purple`, `Yaru-red`, `Yaru-sage`, `Yaru-olive`, `Yaru-magenta`, `Yaru-yellow`, `Yaru-gray`, `Yaru-grey`, `Yaru-wartybrown`. - -## Step 3: Add Backgrounds (Optional) - -Place wallpaper images in a `backgrounds/` directory. Use numbered prefixes to control the default selection order -- the first file alphabetically becomes the default wallpaper. - -``` -backgrounds/ -├── 1-main-wallpaper.jpg # Default wallpaper (first alphabetically) -├── 2-alternate.png # Additional option -└── 3-minimal.jpg # Another option -``` - -Supported formats: `.jpg`, `.png`, `.jpeg`. - -If no backgrounds are provided, the system falls back to the catppuccin default wallpaper. - -## Step 4: Light Theme (Optional) - -For light themes, create an empty `light.mode` marker file: - -```bash -touch themes/palettes/my-theme/light.mode -``` - -This controls Stylix polarity (`light` vs `dark`) and affects GTK theming, browser color scheme, and other system-wide dark/light detection. - -### Step 5: App-Specific Configs (Optional) - -These theme-specific files are automatically picked up by the respective application modules in `features/apps/<app>/default.nix` during theme switching. - -### `apps/btop.theme` - -A btop color theme file. See existing themes for the format, or generate one from your palette colors. - -### `apps/neovim.lua` - -A lazy.nvim plugin spec for the matching Neovim colorscheme: - -```lua -return { - { "sainnhe/everforest" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "everforest", - }, - }, -} -``` - -### `apps/vscode.json` - -VS Code theme extension reference: - -```json -{ - "name": "Everforest Dark", - "extension": "sainnhe.everforest" -} -``` - -### `apps/hyprland.conf` - -Hyprland visual overrides for this theme. This can include custom border colors, gaps, decorations, blur, and animations. The file is sourced after the default settings, so values here override the defaults. - -```conf -$activeBorderColor = rgb(d3c6aa) - -general { - col.active_border = $activeBorderColor - gaps_in = 6 - gaps_out = 12 - border_size = 3 -} - -decoration { - rounding = 10 - blur { - enabled = true - size = 5 - passes = 3 - } -} -``` - -## Step 6: Template Variables - -Nomarchy has a small template system that generates per-palette files from your `colors.toml` at `nomarchy-theme-set` time. Templates in `themes/templates/*.tpl` use placeholder syntax: - -| Syntax | Example Value | Description | -|-------------------------|---------------|------------------------------| -| `{{ background }}` | `#2d353b` | Color value as-is (with `#`) | -| `{{ background_strip }}`| `2d353b` | Color value without `#` | -| `{{ background_rgb }}` | `45,53,59` | Color as decimal RGB | - -Every key from `colors.toml` is available as a template variable. The script (`nomarchy-theme-set-templates`) processes each template into `~/.config/nomarchy/current/theme/<name>` only when no file is already there — so it acts as a fallback for palettes that don't ship the file themselves. - -Two built-in templates ship: - -| Template | Output | Consumed by | -|---------------------|------------------------------------------------|-----------------------------------------------------------------------------| -| `obsidian.css.tpl` | `~/.config/nomarchy/current/theme/obsidian.css` | `nomarchy-theme-set-obsidian` copies it into every Obsidian vault | -| `keyboard.rgb.tpl` | `~/.config/nomarchy/current/theme/keyboard.rgb` | `nomarchy-theme-set-keyboard-asus-rog` calls `asusctl aura effect static` | - -Everything else that used to ship a template (`alacritty.toml.tpl`, `btop.theme.tpl`, `chromium.theme.tpl`, `ghostty.conf.tpl`, `hyprland.conf.tpl`, `hyprlock.conf.tpl`, `kitty.conf.tpl`, `swayosd.css.tpl`, `hyprland-preview-share-picker.css.tpl`) has been removed: those apps are themed via Stylix, declarative Home-Manager options, or per-palette Nix generators in `themes/engine/files.nix` — the template path was always shadowed by an earlier write. - -To add a custom template, drop a `.tpl` file in `~/.config/nomarchy/themes/templates/`. User templates take priority over built-in ones. - -## Step 7: Preview Image (Optional) - -Add a `preview.png` screenshot of the theme in action. This is displayed in the theme selector menu (`nomarchy-menu theme`). - -## Applying Your Theme - -### During development (runtime switch) - -```bash -nomarchy-theme-set my-theme -``` - -This updates `~/.config/nomarchy/state.json`, processes templates, and triggers a Home Manager rebuild. - -### Setting as default (Nix configuration) - -In your `home.nix`: - -```nix -{ - nomarchy.theme = "my-theme"; -} -``` - -Then rebuild: `env-update` (or `sys-update` for system-wide changes). - -## Theme Discovery - -The system automatically discovers themes by scanning `themes/palettes/` for directories containing a `colors.toml` file. No registration step is needed -- just create the directory with a valid `colors.toml` and the theme is available. - -## Checklist - -- [ ] `colors.toml` with all 24 color fields -- [ ] `icons.theme` with a valid GTK icon theme name -- [ ] `backgrounds/` with at least one numbered wallpaper -- [ ] `light.mode` if it's a light theme -- [ ] `apps/neovim.lua` with colorscheme plugin spec -- [ ] `apps/vscode.json` with theme extension reference -- [ ] `apps/btop.theme` with terminal monitor colors -- [ ] `apps/hyprland.conf` if custom decorations/animations are desired -- [ ] `preview.png` for the theme selector diff --git a/features/apps/alacritty/default.nix b/features/apps/alacritty/default.nix deleted file mode 100644 index 3f6f277..0000000 --- a/features/apps/alacritty/default.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.alacritty.enable { - programs.alacritty = { - enable = lib.mkDefault true; - settings = lib.mkDefault { - env = { - TERM = "xterm-256color"; - }; - terminal = { - osc52 = "CopyPaste"; - }; - window = { - padding = { x = 14; y = 14; }; - decorations = "None"; - }; - keyboard = { - bindings = [ - { key = "Insert"; mods = "Shift"; action = "Paste"; } - { key = "Insert"; mods = "Control"; action = "Copy"; } - { key = "Return"; mods = "Shift"; chars = "\\u001B\\r"; } - ]; - }; - }; - }; - }; -} diff --git a/features/apps/btop/config/btop.conf b/features/apps/btop/config/btop.conf deleted file mode 100644 index 0820262..0000000 --- a/features/apps/btop/config/btop.conf +++ /dev/null @@ -1,246 +0,0 @@ -#? Config file for btop - -#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes. -#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes" -color_theme = "nomarchy" - -#* If the theme set background should be shown, set to False if you want terminal background transparency. -theme_background = True - -#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false. -truecolor = True - -#* Set to true to force tty mode regardless if a real tty has been detected or not. -#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols. -force_tty = False - -#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets. -#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box. -#* Use whitespace " " as separator between different presets. -#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty" -presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty" - -#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists. -#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift. -vim_keys = True - -#* Rounded corners on boxes, is ignored if TTY mode is ON. -rounded_corners = True - -#* Default symbols to use for graph creation, "braille", "block" or "tty". -#* "braille" offers the highest resolution but might not be included in all fonts. -#* "block" has half the resolution of braille but uses more common characters. -#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY. -#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view. -graph_symbol = "braille" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_cpu = "default" - -# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty". -graph_symbol_gpu = "default" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_mem = "default" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_net = "default" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_proc = "default" - -#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace. -shown_boxes = "cpu mem net proc" - -#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs. -update_ms = 2000 - -#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct", -#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly. -proc_sorting = "cpu lazy" - -#* Reverse sorting order, True or False. -proc_reversed = False - -#* Show processes as a tree. -proc_tree = False - -#* Use the cpu graph colors in the process list. -proc_colors = True - -#* Use a darkening gradient in the process list. -proc_gradient = True - -#* If process cpu usage should be of the core it's running on or usage of the total available cpu power. -proc_per_core = False - -#* Show process memory as bytes instead of percent. -proc_mem_bytes = True - -#* Show cpu graph for each process. -proc_cpu_graphs = True - -#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate) -proc_info_smaps = False - -#* Show proc box on left side of screen instead of right. -proc_left = False - -#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop). -proc_filter_kernel = False - -#* In tree-view, always accumulate child process resources in the parent process. -proc_aggregate = False - -#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available. -#* Select from a list of detected attributes from the options menu. -cpu_graph_upper = "Auto" - -#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available. -#* Select from a list of detected attributes from the options menu. -cpu_graph_lower = "Auto" - -#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off". -show_gpu_info = "Auto" - -#* Toggles if the lower CPU graph should be inverted. -cpu_invert_lower = True - -#* Set to True to completely disable the lower CPU graph. -cpu_single_graph = False - -#* Show cpu box at bottom of screen instead of top. -cpu_bottom = False - -#* Shows the system uptime in the CPU box. -show_uptime = True - -#* Show cpu temperature. -check_temp = True - -#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors. -cpu_sensor = "Auto" - -#* Show temperatures for cpu cores also if check_temp is True and sensors has been found. -show_coretemp = True - -#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core. -#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine. -#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries. -#* Example: "4:0 5:1 6:3" -cpu_core_map = "" - -#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine". -temp_scale = "celsius" - -#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024. -base_10_sizes = False - -#* Show CPU frequency. -show_cpu_freq = True - -#* Draw a clock at top of screen, formatting according to strftime, empty string to disable. -#* Special formatting: /host = hostname | /user = username | /uptime = system uptime -clock_format = "%X" - -#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort. -background_update = True - -#* Custom cpu model name, empty string to disable. -custom_cpu_name = "" - -#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ". -#* Begin line with "exclude=" to change to exclude filter, otherwise defaults to "most include" filter. Example: disks_filter="exclude=/boot /home/user". -disks_filter = "" - -#* Show graphs instead of meters for memory values. -mem_graphs = True - -#* Show mem box below net box instead of above. -mem_below_net = False - -#* Count ZFS ARC in cached and available memory. -zfs_arc_cached = True - -#* If swap memory should be shown in memory box. -show_swap = True - -#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk. -swap_disk = True - -#* If mem box should be split to also show disks info. -show_disks = True - -#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar. -only_physical = True - -#* Read disks list from /etc/fstab. This also disables only_physical. -use_fstab = True - -#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool) -zfs_hide_datasets = False - -#* Set to true to show available disk space for privileged users. -disk_free_priv = False - -#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view. -show_io_stat = True - -#* Toggles io mode for disks, showing big graphs for disk read/write speeds. -io_mode = False - -#* Set to True to show combined read/write io graphs in io mode. -io_graph_combined = False - -#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ". -#* Example: "/mnt/media:100 /:20 /boot:1". -io_graph_speeds = "" - -#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False. -net_download = 100 - -net_upload = 100 - -#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest. -net_auto = True - -#* Sync the auto scaling for download and upload to whichever currently has the highest scale. -net_sync = True - -#* Starts with the Network Interface specified here. -net_iface = "" - -#* Show battery stats in top right if battery is present. -show_battery = True - -#* Which battery to use if multiple are present. "Auto" for auto detection. -selected_battery = "Auto" - -#* Set loglevel for "~/.config/btop/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG". -#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info. -log_level = "WARNING" - -#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards. -nvml_measure_pcie_speeds = True - -#* Horizontally mirror the GPU graph. -gpu_mirror_graph = True - -#* Custom gpu0 model name, empty string to disable. -custom_gpu_name0 = "" - -#* Custom gpu1 model name, empty string to disable. -custom_gpu_name1 = "" - -#* Custom gpu2 model name, empty string to disable. -custom_gpu_name2 = "" - -#* Custom gpu3 model name, empty string to disable. -custom_gpu_name3 = "" - -#* Custom gpu4 model name, empty string to disable. -custom_gpu_name4 = "" - -#* Custom gpu5 model name, empty string to disable. -custom_gpu_name5 = "" - diff --git a/features/apps/btop/default.nix b/features/apps/btop/default.nix deleted file mode 100644 index 07772cb..0000000 --- a/features/apps/btop/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.btop.enable { - programs.btop.enable = lib.mkDefault true; - xdg.configFile."btop/btop.conf".source = ./config/btop.conf; - }; -} diff --git a/features/apps/elephant/config/calc.toml b/features/apps/elephant/config/calc.toml deleted file mode 100644 index 95d00d1..0000000 --- a/features/apps/elephant/config/calc.toml +++ /dev/null @@ -1 +0,0 @@ -async = false diff --git a/features/apps/elephant/config/desktopapplications.toml b/features/apps/elephant/config/desktopapplications.toml deleted file mode 100644 index de17325..0000000 --- a/features/apps/elephant/config/desktopapplications.toml +++ /dev/null @@ -1,3 +0,0 @@ -show_actions = false -only_search_title = true -history = false diff --git a/features/apps/elephant/config/menus/nomarchy_background_selector.lua b/features/apps/elephant/config/menus/nomarchy_background_selector.lua deleted file mode 100644 index 221903c..0000000 --- a/features/apps/elephant/config/menus/nomarchy_background_selector.lua +++ /dev/null @@ -1,75 +0,0 @@ -Name = "nomarchyBackgroundSelector" -NamePretty = "Nomarchy Background Selector" -Cache = false -HideFromProviderlist = true -SearchName = true - -local function ShellEscape(s) - return "'" .. s:gsub("'", "'\\''") .. "'" -end - -function FormatName(filename) - -- Remove leading number and dash - local name = filename:gsub("^%d+", ""):gsub("^%-", "") - -- Remove extension - name = name:gsub("%.[^%.]+$", "") - -- Replace dashes with spaces - name = name:gsub("-", " ") - -- Capitalize each word - name = name:gsub("%S+", function(word) - return word:sub(1, 1):upper() .. word:sub(2):lower() - end) - return name -end - -function GetEntries() - local entries = {} - local home = os.getenv("HOME") - - -- Read current theme name - local theme_name_file = io.open(home .. "/.config/nomarchy/current/theme.name", "r") - local theme_name = theme_name_file and theme_name_file:read("*l") or nil - if theme_name_file then - theme_name_file:close() - end - - -- Directories to search - local dirs = { - home .. "/.config/nomarchy/current/theme/backgrounds", - } - if theme_name then - table.insert(dirs, home .. "/.config/nomarchy/backgrounds/" .. theme_name) - end - - -- Track added files to avoid duplicates - local seen = {} - - for _, wallpaper_dir in ipairs(dirs) do - local handle = io.popen( - -- -L so -type f follows the home-manager symlinks the backgrounds are - -- deployed as (the nomarchy_themes.lua provider does the same). - "find -L " .. ShellEscape(wallpaper_dir) - .. " -maxdepth 1 -type f \\( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' -o -name '*.gif' -o -name '*.bmp' -o -name '*.webp' \\) 2>/dev/null | sort" - ) - if handle then - for background in handle:lines() do - local filename = background:match("([^/]+)$") - if filename and not seen[filename] then - seen[filename] = true - table.insert(entries, { - Text = FormatName(filename), - Value = background, - Actions = { - activate = "nomarchy-theme-bg-set " .. ShellEscape(background), - }, - Preview = background, - PreviewType = "file", - }) - end - end - handle:close() - end - end - - return entries -end diff --git a/features/apps/elephant/config/menus/nomarchy_themes.lua b/features/apps/elephant/config/menus/nomarchy_themes.lua deleted file mode 100644 index 3749c00..0000000 --- a/features/apps/elephant/config/menus/nomarchy_themes.lua +++ /dev/null @@ -1,95 +0,0 @@ --- --- Dynamic Nomarchy Theme Menu for Elephant/Walker --- -Name = "nomarchythemes" -NamePretty = "Nomarchy Themes" -HideFromProviderlist = true - --- Check if file exists using Lua (no subprocess) -local function file_exists(path) - local f = io.open(path, "r") - if f then - f:close() - return true - end - return false -end - --- Get first matching file from directory using ls (single call for fallback) -local function first_image_in_dir(dir) - local handle = io.popen("ls -1 '" .. dir .. "' 2>/dev/null | head -n 1") - if handle then - local file = handle:read("*l") - handle:close() - if file and file ~= "" then - return dir .. "/" .. file - end - end - return nil -end - --- The main function elephant will call -function GetEntries() - local entries = {} - local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes" - local default_theme_dir = os.getenv("HOME") .. "/.local/share/nomarchy/themes" - - local seen_themes = {} - - -- Helper function to process themes from a directory - local function process_themes_from_dir(theme_dir) - -- Single find call to get all theme directories - local handle = io.popen("find -L '" .. theme_dir .. "' -mindepth 1 -maxdepth 1 -type d 2>/dev/null") - if not handle then - return - end - - for theme_path in handle:lines() do - local theme_name = theme_path:match(".*/(.+)$") - - if theme_name and not seen_themes[theme_name] then - seen_themes[theme_name] = true - - -- Check for preview images directly (no subprocess) - local preview_path = nil - local preview_png = theme_path .. "/preview.png" - local preview_jpg = theme_path .. "/preview.jpg" - - if file_exists(preview_png) then - preview_path = preview_png - elseif file_exists(preview_jpg) then - preview_path = preview_jpg - else - -- Fallback: get first image from backgrounds (one ls call) - preview_path = first_image_in_dir(theme_path .. "/backgrounds") - end - - if preview_path and preview_path ~= "" then - local display_name = theme_name:gsub("_", " "):gsub("%-", " ") - display_name = display_name:gsub("(%a)([%w_']*)", function(first, rest) - return first:upper() .. rest:lower() - end) - display_name = display_name .. " " - - table.insert(entries, { - Text = display_name, - Preview = preview_path, - PreviewType = "file", - Actions = { - activate = "nomarchy-theme-set " .. theme_name, - }, - }) - end - end - end - - handle:close() - end - - -- Process user themes first (they take precedence) - process_themes_from_dir(user_theme_dir) - -- Then process default themes (only if not already seen) - process_themes_from_dir(default_theme_dir) - - return entries -end diff --git a/features/apps/elephant/config/symbols.toml b/features/apps/elephant/config/symbols.toml deleted file mode 100644 index 3144c15..0000000 --- a/features/apps/elephant/config/symbols.toml +++ /dev/null @@ -1 +0,0 @@ -command = 'wl-copy && hyprctl dispatch sendshortcut "SHIFT, Insert,"' diff --git a/features/apps/elephant/default.nix b/features/apps/elephant/default.nix deleted file mode 100644 index defb2f0..0000000 --- a/features/apps/elephant/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.elephant.enable { - xdg.configFile."elephant" = { - source = ./config; - recursive = true; - }; - }; -} diff --git a/features/apps/ghostty/config/config b/features/apps/ghostty/config/config deleted file mode 100644 index 00b6856..0000000 --- a/features/apps/ghostty/config/config +++ /dev/null @@ -1,37 +0,0 @@ -# Dynamic theme colors -config-file = ?"~/.config/nomarchy/current/theme/ghostty.conf" - -# Font -font-family = "JetBrainsMono Nerd Font" -font-style = Regular -font-size = 9 - -# Window -window-theme = ghostty -window-padding-x = 14 -window-padding-y = 14 -confirm-close-surface=false -resize-overlay = never -gtk-toolbar-style = flat - -# Cursor styling -cursor-style = "block" -cursor-style-blink = false - -# Cursor styling + SSH session terminfo -# (all shell integration options must be passed together) -shell-integration-features = no-cursor,ssh-env - -# Keyboard bindings -keybind = shift+insert=paste_from_clipboard -keybind = control+insert=copy_to_clipboard -keybind = super+control+shift+alt+arrow_down=resize_split:down,100 -keybind = super+control+shift+alt+arrow_up=resize_split:up,100 -keybind = super+control+shift+alt+arrow_left=resize_split:left,100 -keybind = super+control+shift+alt+arrow_right=resize_split:right,100 - -# Slowdown mouse scrolling -mouse-scroll-multiplier = 0.95 - -# Fix general slowness on hyprland (https://github.com/ghostty-org/ghostty/discussions/3224) -async-backend = epoll diff --git a/features/apps/ghostty/default.nix b/features/apps/ghostty/default.nix deleted file mode 100644 index 5b7d82b..0000000 --- a/features/apps/ghostty/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.ghostty.enable { - xdg.configFile."ghostty/config".source = ./config/config; - }; -} diff --git a/features/apps/kitty/config/kitty.conf b/features/apps/kitty/config/kitty.conf deleted file mode 100644 index d303609..0000000 --- a/features/apps/kitty/config/kitty.conf +++ /dev/null @@ -1,30 +0,0 @@ -include ~/.config/nomarchy/current/theme/kitty.conf - -# Font -font_family JetBrainsMono Nerd Font -bold_italic_font auto -font_size 9.0 - -# Window -window_padding_width 14 -hide_window_decorations yes -confirm_os_window_close 0 - -# Keybindings -map ctrl+insert copy_to_clipboard -map shift+insert paste_from_clipboard - -# Allow remote access -allow_remote_control yes - -# Aesthetics -cursor_shape block -cursor_blink_interval 0 -shell_integration no-cursor -enable_audio_bell no - -# Minimal Tab bar styling -tab_bar_edge bottom -tab_bar_style powerline -tab_powerline_style slanted -tab_title_template {title}{' :{}:'.format(num_windows) if num_windows > 1 else ''} diff --git a/features/apps/kitty/default.nix b/features/apps/kitty/default.nix deleted file mode 100644 index a7641a6..0000000 --- a/features/apps/kitty/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.kitty.enable { - programs.kitty.enable = lib.mkDefault true; - xdg.configFile."kitty/kitty.conf".source = ./config/kitty.conf; - }; -} diff --git a/features/apps/lazygit/config/config.yml b/features/apps/lazygit/config/config.yml deleted file mode 100644 index e69de29..0000000 diff --git a/features/apps/lazygit/default.nix b/features/apps/lazygit/default.nix deleted file mode 100644 index d49d1b1..0000000 --- a/features/apps/lazygit/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.lazygit.enable { - home.packages = [ pkgs.lazygit ]; - xdg.configFile."lazygit/config.yml".source = ./config/config.yml; - }; -} diff --git a/features/apps/opencode/config/opencode.json b/features/apps/opencode/config/opencode.json deleted file mode 100644 index 4fbe31f..0000000 --- a/features/apps/opencode/config/opencode.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "theme": "system", - "autoupdate": false -} diff --git a/features/apps/opencode/default.nix b/features/apps/opencode/default.nix deleted file mode 100644 index 77acd11..0000000 --- a/features/apps/opencode/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - # Opt-in: the user explicitly wants opencode wiring (config file at - # ~/.config/opencode/opencode.json). Set - # `nomarchy.apps.opencode.enable = true` in your home.nix. - xdg.configFile."opencode/opencode.json" = lib.mkIf config.nomarchy.apps.opencode.enable { - source = ./config/opencode.json; - }; -} diff --git a/features/apps/swayosd.nix b/features/apps/swayosd.nix deleted file mode 100644 index 6ea6c6f..0000000 --- a/features/apps/swayosd.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.swayosd.enable { - services.swayosd = { - enable = lib.mkDefault true; - stylePath = lib.mkDefault "${config.home.homeDirectory}/.config/swayosd/style.css"; - }; - - xdg.configFile."swayosd/style.css".text = lib.mkDefault '' - @define-color background-color #${config.colorScheme.palette.base00}; - @define-color border-color #${config.colorScheme.palette.base0E}; - @define-color label #${config.colorScheme.palette.base05}; - @define-color image #${config.colorScheme.palette.base05}; - @define-color progress #${config.colorScheme.palette.base0B}; - - window { - border-radius: 0; - opacity: 0.97; - border: 2px solid @border-color; - background-color: @background-color; - } - - label { - font-family: '${config.nomarchy.fonts.monospace}'; - font-size: 11pt; - color: @label; - } - - image { - color: @image; - } - - progressbar { - border-radius: 0; - } - - progress { - background-color: @progress; - } - ''; - }; -} diff --git a/features/apps/tmux/config/tmux.conf b/features/apps/tmux/config/tmux.conf deleted file mode 100644 index 083abb6..0000000 --- a/features/apps/tmux/config/tmux.conf +++ /dev/null @@ -1,94 +0,0 @@ -# Prefix -set -g prefix C-Space -set -g prefix2 C-b -bind C-Space send-prefix - -# Reload config -bind q source-file ~/.config/tmux/tmux.conf \; display "Configuration reloaded" - -# Vi mode for copy -setw -g mode-keys vi -bind -T copy-mode-vi v send -X begin-selection -bind -T copy-mode-vi y send -X copy-selection-and-cancel - -# Pane Controls -bind h split-window -v -c "#{pane_current_path}" -bind v split-window -h -c "#{pane_current_path}" -bind x kill-pane - -bind -n C-M-Left select-pane -L -bind -n C-M-Right select-pane -R -bind -n C-M-Up select-pane -U -bind -n C-M-Down select-pane -D - -bind -n C-M-S-Left resize-pane -L 5 -bind -n C-M-S-Down resize-pane -D 5 -bind -n C-M-S-Up resize-pane -U 5 -bind -n C-M-S-Right resize-pane -R 5 - -# Window navigation -bind r command-prompt -I "#W" "rename-window -- '%%'" -bind c new-window -c "#{pane_current_path}" -bind k kill-window - -bind -n M-1 select-window -t 1 -bind -n M-2 select-window -t 2 -bind -n M-3 select-window -t 3 -bind -n M-4 select-window -t 4 -bind -n M-5 select-window -t 5 -bind -n M-6 select-window -t 6 -bind -n M-7 select-window -t 7 -bind -n M-8 select-window -t 8 -bind -n M-9 select-window -t 9 - -bind -n M-Left select-window -t -1 -bind -n M-Right select-window -t +1 -bind -n M-S-Left swap-window -t -1 \; select-window -t -1 -bind -n M-S-Right swap-window -t +1 \; select-window -t +1 - -# Session controls -bind R command-prompt -I "#S" "rename-session -- '%%'" -bind C new-session -c "#{pane_current_path}" -bind K kill-session -bind P switch-client -p -bind N switch-client -n - -bind -n M-Up switch-client -p -bind -n M-Down switch-client -n - -# General -set -g default-terminal "tmux-256color" -set -ag terminal-overrides ",*:RGB" -set -g mouse on -set -g base-index 1 -setw -g pane-base-index 1 -set -g renumber-windows on -set -g history-limit 50000 -set -g escape-time 0 -set -g focus-events on -set -g set-clipboard on -set -g allow-passthrough on -setw -g aggressive-resize on -set -g detach-on-destroy off - -# Status bar -set -g status-position top -set -g status-interval 5 -set -g status-left-length 30 -set -g status-right-length 50 -set -g window-status-separator "" -set -gw automatic-rename on -set -gw automatic-rename-format '#{b:pane_current_path}' - -# Theme -set -g status-style "bg=default,fg=default" -set -g status-left "#[fg=black,bg=blue,bold] #S #[bg=default] " -set -g status-right "#[fg=blue]#{?pane_in_mode,COPY ,}#{?client_prefix,PREFIX ,}#{?window_zoomed_flag,ZOOM ,}#[fg=brightblack]#h " -set -g window-status-format "#[fg=brightblack] #I:#W " -set -g window-status-current-format "#[fg=blue,bold] #I:#W " -set -g pane-border-style "fg=brightblack" -set -g pane-active-border-style "fg=blue" -set -g message-style "bg=default,fg=blue" -set -g message-command-style "bg=default,fg=blue" -set -g mode-style "bg=blue,fg=black" -setw -g clock-mode-colour blue diff --git a/features/apps/tmux/default.nix b/features/apps/tmux/default.nix deleted file mode 100644 index ab5d4fd..0000000 --- a/features/apps/tmux/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.tmux.enable { - programs.tmux.enable = lib.mkDefault true; - xdg.configFile."tmux/tmux.conf".source = ./config/tmux.conf; - }; -} diff --git a/features/apps/vscode.nix b/features/apps/vscode.nix deleted file mode 100644 index fbfef15..0000000 --- a/features/apps/vscode.nix +++ /dev/null @@ -1,118 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - themePath = ../../themes/palettes + "/${config.nomarchy.theme}/apps/vscode.json"; - themeConfig = if builtins.pathExists themePath then - builtins.fromJSON (builtins.readFile themePath) - else - { name = "Default Dark Modern"; }; - - # Theme extensions matching palette vscode.json `extension` fields. - # Always installed because workbench.colorTheme is set unconditionally - # from the active palette — without the matching extension VSCode - # silently falls back to its default theme. - # - # The list is split because nixpkgs doesn't package every theme our - # palettes use. Six are in pkgs.vscode-extensions; the rest are pulled - # from the VSCode marketplace via extensionFromVscodeMarketplace with - # publisher / name / version / sha256 pinned. To refresh a version, - # bump the `version` and `nix-prefetch-url` the new .vsix: - # - # URL='https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${name}/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage' - # nix store prefetch-file --hash-type sha256 "$URL" - # - # Three palette extensions remain unfixed because the corresponding - # marketplace entries don't exist — Bjarne.ethereal-nomarchy, - # Bjarne.hackerman-nomarchy, Bjarne.vantablack-nomarchy. The matching - # palettes (ethereal, hackerman, vantablack) still fall back to - # VSCode's default theme; see the ROADMAP Later row for next steps. - - marketplaceExtensions = with pkgs.vscode-utils; [ - (extensionFromVscodeMarketplace { # everforest, summer-day, summer-night - publisher = "sainnhe"; name = "everforest"; version = "0.3.0"; - sha256 = "sha256-nZirzVvM160ZTpBLTimL2X35sIGy5j2LQOok7a2Yc7U="; - }) - (extensionFromVscodeMarketplace { # flexoki-light - publisher = "shadesOfBuntu"; name = "flexoki-light"; version = "1.0.0"; - sha256 = "sha256-//y9uvIUGGDEhd8inDHvNy2e1IKNx6hSfO9sxNOTcUE="; - }) - (extensionFromVscodeMarketplace { # kanagawa - publisher = "qufiwefefwoyn"; name = "kanagawa"; version = "1.5.1"; - sha256 = "sha256-AGGioXcK/fjPaFaWk2jqLxovUNR59gwpotcSpGNbj1c="; - }) - (extensionFromVscodeMarketplace { # lumon - publisher = "oldjobobo"; name = "lumon-theme"; version = "0.0.7"; - sha256 = "sha256-YlO1r1JjaumiicvMk5fBr+PZCYFaII03PaLiyqE35Go="; - }) - (extensionFromVscodeMarketplace { # matte-black - publisher = "TahaYVR"; name = "matteblack"; version = "1.0.3"; - sha256 = "sha256-wn/llGidzyPd91XT3xVqAvaU4NcTTggTSz8WmUcV8HM="; - }) - (extensionFromVscodeMarketplace { # miasma - publisher = "oldjobobo"; name = "miasma-theme"; version = "0.1.1"; - sha256 = "sha256-STFTGFhQqxocr+YNFQp36IQWJRKTDBgBg4MOCOq1IPQ="; - }) - (extensionFromVscodeMarketplace { # osaka-jade - publisher = "jovejonovski"; name = "ocean-green"; version = "1.1.2"; - sha256 = "sha256-sUNjnzqXya23Uieg8RLcEfnxiX0ImZ6CIjFtSJ66vM4="; - }) - (extensionFromVscodeMarketplace { # retro-82 - publisher = "oldjobobo"; name = "retro-82-theme"; version = "0.1.4"; - sha256 = "sha256-GqFeFFG5scO7CEXlKjj6uoilCoUfpRaQa5jBSnNJyJA="; - }) - (extensionFromVscodeMarketplace { # ristretto - publisher = "monokai"; name = "theme-monokai-pro-vscode"; version = "2.0.13"; - sha256 = "sha256-5bKwVzDfZoSipR04tPDx9jbKhYsSsa3z6Ei9E2jhudo="; - }) - (extensionFromVscodeMarketplace { # white - publisher = "Bjarne"; name = "white-theme"; version = "0.0.1"; - sha256 = "sha256-3ZyWNVlQO3Tof49FFF3OImuo7hgtJXLNuwQ+iHjzzGk="; - }) - ]; - - themeExtensions = (with pkgs.vscode-extensions; [ - catppuccin.catppuccin-vsc # catppuccin, catppuccin-latte - enkia.tokyo-night # tokyo-night - arcticicestudio.nord-visual-studio-code # nord - mvllow.rose-pine # rose-pine - jdinhlife.gruvbox # gruvbox - ]) ++ marketplaceExtensions; - - # Development extensions — opt-in via nomarchy.vscode.devExtensions. - devExtensions = with pkgs.vscode-extensions; [ - # Language support - ms-python.python - rust-lang.rust-analyzer - golang.go - jnoortheen.nix-ide - - # Git integration - eamodio.gitlens - - # Editor enhancements - esbenp.prettier-vscode - dbaeumer.vscode-eslint - bradlc.vscode-tailwindcss - ]; -in -{ - config = lib.mkIf config.nomarchy.apps.vscode.enable { - programs.vscode = { - enable = lib.mkDefault true; - package = lib.mkDefault pkgs.vscode; - profiles.default = { - userSettings = lib.mkDefault { - "update.mode" = "none"; - "workbench.colorTheme" = themeConfig.name; - "window.titleBarStyle" = "custom"; - "editor.fontFamily" = "'${config.nomarchy.fonts.monospace}', 'Droid Sans Mono', monospace"; - "terminal.integrated.fontFamily" = config.nomarchy.fonts.monospace; - }; - extensions = lib.mkDefault ( - themeExtensions - ++ lib.optionals config.nomarchy.apps.vscode.devExtensions devExtensions - ); - }; - }; - }; -} diff --git a/features/apps/walker.nix b/features/apps/walker.nix deleted file mode 100644 index 11ea3e6..0000000 --- a/features/apps/walker.nix +++ /dev/null @@ -1,51 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - config = lib.mkIf config.nomarchy.apps.walker.enable { - home.packages = [ pkgs.lua ]; - - programs.walker = { - enable = lib.mkDefault true; - runAsService = lib.mkDefault true; - config = lib.mkDefault { - theme = "nomarchy"; - ui = { - anchors = { - top = true; - }; - }; - selection_wrap = true; - hide_action_hints = true; - placeholders = { - "default" = { input = " Search..."; list = "No Results"; }; - }; - keybinds = { - quick_activate = []; - }; - columns = { - symbols = 1; - }; - providers = { - max_results = 256; - default = [ - "desktopapplications" - "websearch" - ]; - prefixes = [ - { prefix = "/"; provider = "providerlist"; } - { prefix = "."; provider = "files"; } - { prefix = ":"; provider = "symbols"; } - { prefix = "="; provider = "calc"; } - { prefix = "@"; provider = "websearch"; } - { prefix = "$"; provider = "clipboard"; } - ]; - }; - }; - themes."nomarchy" = lib.mkDefault { - style = '' - @import "${config.home.homeDirectory}/.config/nomarchy/current/theme/apps/walker/style.css"; - ''; - }; - }; - }; -} diff --git a/features/default.nix b/features/default.nix deleted file mode 100644 index 7332423..0000000 --- a/features/default.nix +++ /dev/null @@ -1,78 +0,0 @@ -{ config, pkgs, inputs, lib, ... }: - -let - nomarchyLib = import ../lib { inherit lib; }; -in -{ - imports = [ - inputs.nix-colors.homeManagerModules.default - inputs.walker.homeManagerModules.default - ../core/home - ../themes/engine/stylix-compat.nix - ../themes/engine/loader.nix - ../themes/engine/files.nix - ../themes/engine/scripts.nix - ../themes/engine/stylix.nix - ./apps/alacritty/default.nix - ./apps/btop/default.nix - ./apps/elephant/default.nix - ./apps/ghostty/default.nix - ./apps/kitty/default.nix - ./apps/lazygit/default.nix - ./apps/opencode/default.nix - ./apps/tmux/default.nix - ./apps/vscode.nix - ./apps/walker.nix - ./apps/swayosd.nix - ./desktop/nightlight.nix - ./desktop/idle.nix - ./desktop/hyprland/default.nix - ./desktop/waybar/default.nix - ./scripts/default.nix - ./scripts/battery-monitor.nix - ]; - - - colorScheme = lib.mkDefault (nomarchyLib.getColorScheme config.nomarchy.theme); - - # Enable neovim program module (required for stylix integration) - programs.neovim.enable = lib.mkDefault true; - - # NOT mkDefault: home.packages is a list that MERGES across modules. At - # mkDefault priority this whole curated list is dropped the moment any - # other module sets home.packages at normal priority (features/scripts, - # the installer's profile packages), silently removing firefox, mako, - # hyprlock, mpv, etc. — which broke notifications and the lock screen. - home.packages = (with pkgs; [ - # Hyprland ecosystem - swww # Wallpaper daemon - mako # Notification daemon - hyprlock # Lock screen - - # Screenshot and clipboard - wl-clipboard - grim - slurp - - # Hardware control - brightnessctl - playerctl - pamixer - - # Utilities - jq - xmlstarlet - mise - gum # TUI components for scripts - 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 - # so switching to e.g. summer-night auto-installs everforest-gtk-theme. - bibata-cursors - ]); -} - diff --git a/features/desktop/hyprland/config/bindings.conf b/features/desktop/hyprland/config/bindings.conf deleted file mode 100644 index 6f066a7..0000000 --- a/features/desktop/hyprland/config/bindings.conf +++ /dev/null @@ -1,33 +0,0 @@ -# Application bindings -bindd = SUPER, RETURN, Terminal, exec, uwsm-app -- xdg-terminal-exec || uwsm-app -- alacritty -bindd = SUPER ALT, RETURN, Tmux, exec, uwsm-app -- xdg-terminal-exec bash -c "tmux attach || tmux new -s Work" -bindd = SUPER SHIFT, RETURN, Browser, exec, nomarchy-launch-browser -bindd = SUPER SHIFT, F, File manager, exec, uwsm-app -- thunar --new-window -bindd = SUPER ALT SHIFT, F, File manager (cwd), exec, uwsm-app -- thunar --new-window "$(nomarchy-cmd-terminal-cwd)" -bindd = SUPER SHIFT, B, Browser, exec, nomarchy-launch-browser -bindd = SUPER SHIFT ALT, B, Browser (private), exec, nomarchy-launch-browser --private -bindd = SUPER SHIFT, M, Music, exec, nomarchy-launch-or-focus spotify -bindd = SUPER SHIFT, N, Editor, exec, nomarchy-launch-editor -bindd = SUPER SHIFT, D, Docker, exec, nomarchy-launch-tui lazydocker -bindd = SUPER SHIFT, G, Signal, exec, nomarchy-launch-or-focus ^signal$ "uwsm-app -- signal-desktop" -bindd = SUPER SHIFT, O, Obsidian, exec, nomarchy-launch-or-focus ^obsidian$ "uwsm-app -- obsidian -disable-gpu --enable-wayland-ime" -bindd = SUPER SHIFT, SLASH, Passwords, exec, uwsm-app -- 1password - -# If your web app url contains #, type it as ## to prevent hyprland treating it as a comment -bindd = SUPER SHIFT, A, ChatGPT, exec, nomarchy-launch-webapp "https://chatgpt.com" -bindd = SUPER SHIFT ALT, A, Grok, exec, nomarchy-launch-webapp "https://grok.com" -bindd = SUPER SHIFT, C, Calendar, exec, nomarchy-launch-webapp "https://app.hey.com/calendar/weeks/" -bindd = SUPER SHIFT, E, Email, exec, nomarchy-launch-webapp "https://app.hey.com" -bindd = SUPER SHIFT, Y, YouTube, exec, nomarchy-launch-webapp "https://youtube.com/" -bindd = SUPER SHIFT ALT, G, WhatsApp, exec, nomarchy-launch-or-focus-webapp WhatsApp "https://web.whatsapp.com/" -bindd = SUPER SHIFT CTRL, G, Google Messages, exec, nomarchy-launch-or-focus-webapp "Google Messages" "https://messages.google.com/web/conversations" -bindd = SUPER SHIFT, P, Google Photos, exec, nomarchy-launch-or-focus-webapp "Google Photos" "https://photos.google.com/" -bindd = SUPER SHIFT, X, X, exec, nomarchy-launch-webapp "https://x.com/" -bindd = SUPER SHIFT ALT, X, X Post, exec, nomarchy-launch-webapp "https://x.com/compose/post" - -# Add extra bindings -# bind = SUPER SHIFT, R, exec, alacritty -e ssh your-server - -# Overwrite existing bindings, like putting Nomarchy Menu back on Super + Space -# unbind = SUPER, SPACE -# bindd = SUPER, SPACE, Nomarchy menu, exec, nomarchy-menu diff --git a/features/desktop/hyprland/config/input.conf b/features/desktop/hyprland/config/input.conf deleted file mode 100644 index 4c069a1..0000000 --- a/features/desktop/hyprland/config/input.conf +++ /dev/null @@ -1,53 +0,0 @@ -# Control your input devices -# See https://wiki.hypr.land/Configuring/Variables/#input -input { - # Use multiple keyboard layouts and switch between them with Left Alt + Right Alt - # kb_layout = us,dk,eu - - # Use a specific keyboard variant if needed (e.g. intl for international keyboards) - # kb_variant = intl - - kb_options = compose:caps # ,grp:alts_toggle - - # Change speed of keyboard repeat - repeat_rate = 40 - repeat_delay = 600 - - # Start with numlock on by default - numlock_by_default = true - - # Increase sensitivity for mouse/trackpad (default: 0) - # sensitivity = 0.35 - - # Turn off mouse acceleration (default: false) - # force_no_accel = true - - touchpad { - # Use natural (inverse) scrolling - # natural_scroll = true - - # Use two-finger clicks for right-click instead of lower-right corner - # clickfinger_behavior = true - - # Control the speed of your scrolling - scroll_factor = 0.4 - - # Enable the touchpad while typing - # disable_while_typing = false - - # Left-click-and-drag with three fingers - # drag_3fg = 1 - } -} - -# Scroll nicely in the terminal -#windowrulev2 = match:class (Alacritty|kitty), scroll_touchpad 1.5 -#windowrulev2 = match:class com.mitchellh.ghostty, scroll_touchpad 0.2 - -# Enable touchpad gestures for changing workspaces -# See https://wiki.hyprland.org/Configuring/Gestures/ -# gesture = 3, horizontal, workspace - -# Enable touchpad gestures for moving focus (helpful on scrolling layout) -# gesture = 3, left, dispatcher, movefocus, l -# gesture = 3, right, dispatcher, movefocus, r diff --git a/features/desktop/hyprland/config/nomarchy.conf b/features/desktop/hyprland/config/nomarchy.conf deleted file mode 100644 index 3d7339a..0000000 --- a/features/desktop/hyprland/config/nomarchy.conf +++ /dev/null @@ -1,45 +0,0 @@ -# Learn how to configure Hyprland: https://wiki.hyprland.org/Configuring/ - -# ============================================================================ -# BEHAVIOR CONFIGS (keybindings, input, rules - non-visual) -# ============================================================================ -# These are the Nomarchy defaults - don't edit these directly! - -# Environment variables -source = ~/.config/nomarchy/default/hypr/envs.conf - -# Keybindings -source = ~/.config/hypr/bindings.conf -source = ~/.config/nomarchy/default/hypr/bindings/media.conf -source = ~/.config/nomarchy/default/hypr/bindings/clipboard.conf -source = ~/.config/nomarchy/default/hypr/bindings/tiling-v2.conf -source = ~/.config/nomarchy/default/hypr/bindings/utilities.conf - -# Input settings -source = ~/.config/nomarchy/default/hypr/input.conf - -# Window rules and layout -source = ~/.config/nomarchy/default/hypr/windows.conf - -# Autostart applications -source = ~/.config/nomarchy/default/hypr/autostart.conf - -# Look and feel (animations, layout behavior) -source = ~/.config/nomarchy/default/hypr/looknfeel.conf - -# ============================================================================ -# VISUAL CONFIGS (colors, theming) -# ============================================================================ -# Theme colors - loaded from the currently active theme -source = ~/.config/nomarchy/current/theme/hyprland.conf -# ============================================================================ -# USER OVERRIDES -# ============================================================================ -# Your personal overrides - edit these files to customize Nomarchy! - -source = ~/.config/hypr/monitors.conf -source = ~/.config/hypr/input.conf - -# Add any other personal Hyprland configuration below - -# windowrule = workspace 5, match:class qemu diff --git a/features/desktop/hyprland/default.nix b/features/desktop/hyprland/default.nix deleted file mode 100644 index 778795e..0000000 --- a/features/desktop/hyprland/default.nix +++ /dev/null @@ -1,83 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - nomarchyLib = import ../../../lib { inherit lib; }; - assetsPath = ../../../themes/palettes; - - # Use shared wallpaper resolver - activeWallpaper = nomarchyLib.resolveWallpaper { - wallpaperPath = config.nomarchy.wallpaper; - themeName = config.nomarchy.theme; - inherit assetsPath; - }; - - hyprlandState = config.nomarchy.hyprland; -in -{ - home.sessionVariables = lib.mkDefault { - WLR_NO_HARDWARE_CURSORS = "1"; - HYPRLAND_LOG_WLR = "1"; - }; - - wayland.windowManager.hyprland = { - enable = lib.mkDefault true; - systemd.enable = lib.mkDefault false; - settings = lib.mkDefault { - "debug" = { - "disable_logs" = false; - "enable_stdout_logs" = true; - }; - "cursor" = { - "no_hardware_cursors" = true; - }; - "general" = { - "gaps_in" = hyprlandState.gaps_in; - "gaps_out" = hyprlandState.gaps_out; - "border_size" = hyprlandState.border_size; - "col.active_border" = "rgb(${config.colorScheme.palette.base0E})"; - "col.inactive_border" = "rgb(${config.colorScheme.palette.base03})"; - }; - }; - # Not mkDefault: this is the entry point that wires the rest of Nomarchy's - # Hyprland config tree. mkDefault here gets silently replaced by any host - # that adds extraConfig with mkAfter/normal priority, dropping all bindings, - # autostart, theme colors, etc. — see live-ISO regression where this hid - # every keybinding behind two welcome notifications. - extraConfig = '' - source = ~/.config/hypr/nomarchy.conf - ''; - }; - - # Deploy Hyprland configuration files. Only the files that nomarchy.conf - # actually sources are deployed here — looknfeel.conf and autostart.conf - # live under ~/.config/nomarchy/default/hypr/ and are deployed by the - # core/home bulk-nomarchy dir, so duplicating them here was dead surface. - xdg.configFile."hypr/nomarchy.conf".source = ./config/nomarchy.conf; - xdg.configFile."hypr/monitors.conf".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; - - # Run swaybg as a proper systemd user service rather than a Hyprland exec-once. - # exec-once fails silently (black screen with no visible error) when timing - # against uwsm's graphical-session target is off; a service surfaces failures - # via `systemctl --user status nomarchy-wallpaper` and auto-restarts. - systemd.user.services.nomarchy-wallpaper = { - Unit = { - Description = "Nomarchy desktop wallpaper (swaybg)"; - PartOf = [ "graphical-session.target" ]; - After = [ "graphical-session.target" ]; - }; - Service = { - ExecStart = "${pkgs.swaybg}/bin/swaybg -i %h/.config/nomarchy/current/background -m fill"; - Restart = "on-failure"; - RestartSec = "2"; - }; - Install = { - WantedBy = [ "graphical-session.target" ]; - }; - }; -} diff --git a/features/desktop/hyprland/themes/kanagawa/hyprland.conf b/features/desktop/hyprland/themes/kanagawa/hyprland.conf deleted file mode 100644 index 0ee92ca..0000000 --- a/features/desktop/hyprland/themes/kanagawa/hyprland.conf +++ /dev/null @@ -1,12 +0,0 @@ -$activeBorderColor = rgb(dcd7ba) - -general { - col.active_border = $activeBorderColor -} - -group { - col.border_active = $activeBorderColor -} - -# Kanagawa backdrop is too strong for detault opacity -windowrule = opacity 0.98 0.95, match:tag terminal diff --git a/features/desktop/hyprland/themes/lumon/hyprland.conf b/features/desktop/hyprland/themes/lumon/hyprland.conf deleted file mode 100644 index 40d21b4..0000000 --- a/features/desktop/hyprland/themes/lumon/hyprland.conf +++ /dev/null @@ -1,26 +0,0 @@ -$activeBorderColor = rgb(f2fcff) -$activeShadowColor = rgb(6fb8e3) -$inactiveBorderColor = rgba(30486099) -$inactiveShadowColor = rgba(30486077) - -general { - col.active_border = $activeBorderColor - col.inactive_border = $inactiveBorderColor - gaps_in = 8 - gaps_out = 16 -} - -group { - col.border_active = $activeBorderColor - col.border_inactive = $inactiveBorderColor -} - -decoration { - shadow { - enabled = true - range = 16 - render_power = 4 - color = $activeShadowColor - color_inactive = $inactiveShadowColor - } -} diff --git a/features/desktop/hyprland/themes/nord/hyprland.conf b/features/desktop/hyprland/themes/nord/hyprland.conf deleted file mode 100644 index daeb243..0000000 --- a/features/desktop/hyprland/themes/nord/hyprland.conf +++ /dev/null @@ -1,9 +0,0 @@ -$activeBorderColor = rgb(81a1c1) - -general { - col.active_border = $activeBorderColor -} - -group { - col.border_active = $activeBorderColor -} diff --git a/features/desktop/hyprland/themes/retro-82/hyprland.conf b/features/desktop/hyprland/themes/retro-82/hyprland.conf deleted file mode 100644 index 4d4c6ad..0000000 --- a/features/desktop/hyprland/themes/retro-82/hyprland.conf +++ /dev/null @@ -1,9 +0,0 @@ -$activeBorderColor = rgb(faa968) - -general { - col.active_border = $activeBorderColor -} - -group { - col.border_active = $activeBorderColor -} diff --git a/features/desktop/hyprland/themes/summer-day/hyprland.conf b/features/desktop/hyprland/themes/summer-day/hyprland.conf deleted file mode 100644 index bc5a253..0000000 --- a/features/desktop/hyprland/themes/summer-day/hyprland.conf +++ /dev/null @@ -1,72 +0,0 @@ -$bg_dim = 0xffefebd4 -$bg0 = 0xfffdf6e3 -$bg1 = 0xfff4f0d9 -$bg2 = 0xffefebd4 -$bg3 = 0xffe6e2cc -$bg4 = 0xffe0dcc7 -$bg5 = 0xffbdc3af -$bg_visual = 0xffeaedc8 -$bg_red = 0xfffbe3da -$bg_green = 0xfff0f1d2 -$bg_blue = 0xffe9f0e9 -$bg_yellow = 0xfffaedcd -$fg = 0xff5c6a72 -$red = 0xfff85552 -$orange = 0xfff57d26 -$yellow = 0xffdfa000 -$green = 0xff8da101 -$aqua = 0xff35a77c -$blue = 0xff3a94c5 -$purple = 0xffdf69ba -$grey0 = 0xffa6b0a0 -$grey1 = 0xff939f91 -$grey2 = 0xff829181 - -general { - gaps_in = 6 - gaps_out = 12 - border_size = 3 - col.active_border = $fg - col.inactive_border = $bg5 - layout = dwindle - resize_on_border = true -} - -decoration { - rounding = 10 - -blur { - enabled = true - size = 5 - passes = 3 - new_optimizations = true - ignore_opacity = true -} - -shadow { - enabled = true - range = 20 - render_power = 3 - color = rgb(2e3538) - color_inactive = rgb(61694f) - scale = 1.0 - offset = 0 10 -} - - -} - -animations { - enabled = true - - bezier = overshot, 0.05, 0.9, 0.1, 1.05 - bezier = smoothOut, 0.36, 0, 0.66, -0.56 - bezier = smoothIn, 0.25, 1, 0.5, 1 - - animation = windows, 1, 3, overshot, slide - animation = windowsOut, 1, 3, smoothOut, slide - animation = windowsMove, 1, 3, default - animation = border, 1, 3, default - animation = fade, 1, 3, smoothIn - animation = workspaces, 1, 3, smoothIn, slide -} diff --git a/features/desktop/hyprland/themes/summer-night/hyprland.conf b/features/desktop/hyprland/themes/summer-night/hyprland.conf deleted file mode 100644 index 8573914..0000000 --- a/features/desktop/hyprland/themes/summer-night/hyprland.conf +++ /dev/null @@ -1,61 +0,0 @@ -# --- Everforest Color Palette --- -$bg0 = rgba(2d353bee) -$bg1 = rgba(343f44ee) -$bg2 = rgba(3d484dee) -$bg3 = rgba(475258ee) -$bg4 = rgba(4f585eee) -$bg5 = rgba(56635fee) -$fg = rgba(d3c6aaee) -$red = rgba(e67e80ee) -$orange = rgba(e69875ee) -$yellow = rgba(dbbc7fee) -$green = rgba(a7c080ee) -$aqua = rgba(83c092ee) -$blue = rgba(7fbbb3ee) -$purple = rgba(d699b6ee) -$grey0 = rgba(7a8478ee) -$grey1 = rgba(859289ee) -$grey2 = rgba(9da9a0ee) - -general { - gaps_in = 6 - gaps_out = 12 - border_size = 3 - col.active_border = $fg - col.inactive_border = $bg5 - layout = dwindle - resize_on_border = true -} - -decoration { - rounding = 10 - - blur { - enabled = true - size = 5 - passes = 3 - new_optimizations = true - ignore_opacity = true - } - - shadow { - enabled = true - range = 20 - render_power = 3 - color = rgba(00000044) - } -} - -animations { - enabled = true - bezier = overshot, 0.05, 0.9, 0.1, 1.05 - bezier = smoothOut, 0.36, 0, 0.66, -0.56 - bezier = smoothIn, 0.25, 1, 0.5, 1 - - animation = windows, 1, 3, overshot, slide - animation = windowsOut, 1, 3, smoothOut, slide - animation = windowsMove, 1, 3, default - animation = border, 1, 3, default - animation = fade, 1, 3, smoothIn - animation = workspaces, 1, 3, smoothIn, slide -} diff --git a/features/desktop/idle.nix b/features/desktop/idle.nix deleted file mode 100644 index a36c49c..0000000 --- a/features/desktop/idle.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ config, lib, ... }: - -{ - services.hypridle = { - enable = lib.mkDefault config.nomarchy.toggles.idle; - settings = lib.mkDefault { - general = { - lock_cmd = "nomarchy-lock-screen"; - before_sleep_cmd = "loginctl lock-session"; - after_sleep_cmd = "sleep 1 && hyprctl dispatch dpms on"; - inhibit_sleep = 3; - }; - - listener = [ - { - timeout = 150; - on-timeout = "pidof hyprlock || nomarchy-launch-screensaver"; - } - { - timeout = 151; - on-timeout = "loginctl lock-session"; - } - { - timeout = 330; - on-timeout = "brightnessctl -sd '*::kbd_backlight' set 0"; - on-resume = "brightnessctl -rd '*::kbd_backlight'"; - } - { - timeout = 330; - on-timeout = "hyprctl dispatch dpms off"; - on-resume = "hyprctl dispatch dpms on && brightnessctl -r"; - } - ]; - }; - }; -} diff --git a/features/desktop/nightlight.nix b/features/desktop/nightlight.nix deleted file mode 100644 index 23f6d50..0000000 --- a/features/desktop/nightlight.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ config, lib, ... }: - -# hyprsunset (the blue-light filter) is gated on `nomarchy.toggles.nightlight` -# so a disabled toggle means no process runs — symmetric with how -# `services.hypridle.enable` is wired off `toggles.idle`. The temperature -# comes from `nomarchy.nightlightTemperature`, evaluated at Nix-eval time -# and baked into `extraArgs`. The toggle script (themes/engine/scripts/ -# nomarchy-toggle-nightlight) writes both the toggle and the same -# temperature value into state.json and flips the systemd unit for -# instant feedback; the next rebuild's HM activation realigns systemd -# with the persistent state. - -{ - services.hyprsunset = { - enable = lib.mkDefault config.nomarchy.toggles.nightlight; - extraArgs = lib.mkDefault [ - "--temperature" - (toString config.nomarchy.nightlightTemperature) - ]; - }; -} diff --git a/features/desktop/waybar/config/config.jsonc b/features/desktop/waybar/config/config.jsonc deleted file mode 100644 index c67b031..0000000 --- a/features/desktop/waybar/config/config.jsonc +++ /dev/null @@ -1,149 +0,0 @@ -{ - "reload_style_on_change": true, - "layer": "top", - "position": "top", - "spacing": 0, - "height": 26, - "modules-left": ["custom/nomarchy", "hyprland/workspaces"], - "modules-center": ["clock", "custom/update", "custom/screenrecording-indicator", "custom/idle-indicator", "custom/notification-silencing-indicator"], - "modules-right": [ - "tray", - "bluetooth", - "network", - "pulseaudio", - "cpu", - "battery" - ], - "hyprland/workspaces": { - "on-click": "activate", - "format": "{icon}", - "format-icons": { - "default": "", - "1": "1", - "2": "2", - "3": "3", - "4": "4", - "5": "5", - "6": "6", - "7": "7", - "8": "8", - "9": "9", - "10": "0", - "active": "󱓻" - }, - "persistent-workspaces": { - "1": [], - "2": [], - "3": [], - "4": [], - "5": [] - } - }, - "custom/nomarchy": { - "format": "", - "on-click": "nomarchy-menu", - "on-click-right": "xdg-terminal-exec", - "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 - }, - - "cpu": { - "interval": 5, - "format": "󰍛", - "on-click": "nomarchy-launch-or-focus-tui btop", - "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": 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": { - "format-icons": ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"], - "format": "{icon}", - "format-wifi": "{icon}", - "format-ethernet": "󰀂", - "format-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" - }, - "battery": { - "format": "{capacity}% {icon}", - "format-discharging": "{icon}", - "format-charging": "{icon}", - "format-plugged": "", - "format-icons": { - "charging": ["󰢜", "󰂆", "󰂇", "󰂈", "󰢝", "󰂉", "󰢞", "󰂊", "󰂋", "󰂅"], - "default": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"] - }, - "format-full": "󰂅", - "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": { - "warning": 20, - "critical": 10 - } - }, - "bluetooth": { - "format": "", - "format-off": "󰂲", - "format-disabled": "󰂲", - "format-connected": "󰂱", - "format-no-controller": "", - "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}%\n\nSuper + Ctrl + A", - "scroll-step": 5, - "format-muted": "", - "format-icons": { - "headphone": "", - "headset": "", - "default": ["", "", ""] - } - }, - "custom/screenrecording-indicator": { - "on-click": "nomarchy-cmd-screenrecord", - "exec": "~/.config/nomarchy/default/waybar/indicators/screen-recording.sh", - "signal": 8, - "return-type": "json" - }, - "custom/idle-indicator": { - "on-click": "nomarchy-toggle-idle", - "exec": "~/.config/nomarchy/default/waybar/indicators/idle.sh", - "signal": 9, - "return-type": "json" - }, - "custom/notification-silencing-indicator": { - "on-click": "nomarchy-toggle-notification-silencing", - "exec": "~/.config/nomarchy/default/waybar/indicators/notification-silencing.sh", - "signal": 10, - "return-type": "json" - }, - "tray": { - "icon-size": 12, - "spacing": 17 - } -} diff --git a/features/desktop/waybar/config/style.css b/features/desktop/waybar/config/style.css deleted file mode 100644 index 88a83cb..0000000 --- a/features/desktop/waybar/config/style.css +++ /dev/null @@ -1,99 +0,0 @@ -@import "../nomarchy/current/theme/waybar.css"; - -* { - background-color: @background; - color: @foreground; - - border: none; - border-radius: 0; - min-height: 0; - font-family: 'JetBrainsMono Nerd Font'; - font-size: 12px; -} - -.modules-left { - margin-left: 8px; -} - -.modules-right { - margin-right: 8px; -} - -#workspaces button { - all: initial; - padding: 0 6px; - margin: 0 1.5px; - min-width: 9px; -} - -#workspaces button.empty { - opacity: 0.5; -} - -#cpu, -#battery, -#pulseaudio, -#custom-nomarchy, -#custom-update { - min-width: 12px; - margin: 0 7.5px; -} - -/* The Nomarchy glyph lives at U+F000 in the dedicated "Nomarchy" font. - Nerd Fonts (used by default) also have U+F000 but render it as a glass - icon, so we have to pin the family for this module. */ -#custom-nomarchy { - font-family: Nomarchy; - font-size: 20px; -} - -#tray { - margin-right: 16px; -} - -#bluetooth { - margin-right: 17px; -} - -#network { - margin-right: 13px; -} - -#custom-expand-icon { - margin-right: 18px; -} - -tooltip { - padding: 2px; -} - -#custom-update { - font-size: 10px; -} - -#clock { - margin-left: 8.75px; -} - -.hidden { - opacity: 0; -} - -#custom-screenrecording-indicator, -#custom-idle-indicator, -#custom-notification-silencing-indicator { - min-width: 12px; - margin-left: 5px; - margin-right: 0; - font-size: 10px; - padding-bottom: 1px; -} - -#custom-screenrecording-indicator.active { - color: #a55555; -} - -#custom-idle-indicator.active, -#custom-notification-silencing-indicator.active { - color: #a55555; -} diff --git a/features/desktop/waybar/default.nix b/features/desktop/waybar/default.nix deleted file mode 100644 index 3d55389..0000000 --- a/features/desktop/waybar/default.nix +++ /dev/null @@ -1,56 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - activeTheme = config.nomarchy.theme; - - # Path to local theme files - themeDir = ./themes + "/${activeTheme}"; - hasThemeConfig = builtins.pathExists (themeDir + "/config.jsonc"); - hasThemeStyle = builtins.pathExists (themeDir + "/style.css"); - - # Default fallback files - defaultConfig = ./config/config.jsonc; - defaultStyle = ./config/style.css; - - # Selected files - configFile = if hasThemeConfig then (themeDir + "/config.jsonc") else defaultConfig; - styleFile = if hasThemeStyle then (themeDir + "/style.css") else defaultStyle; - - rawSettings = builtins.fromJSON (builtins.readFile configFile); - - # Modules that only make sense on a laptop. Filtered out of any - # `modules-*` slot when nomarchy.formFactor != "laptop" so a desktop - # build doesn't ship a permanently-empty battery indicator. - laptopOnlyModules = [ "battery" "custom/battery" ]; - filterModules = mods: - if config.nomarchy.formFactor == "laptop" - then mods - else builtins.filter (m: !(builtins.elem m laptopOnlyModules)) mods; - - settings = rawSettings // { - position = config.nomarchy.panelPosition; - modules-left = filterModules (rawSettings.modules-left or []); - modules-center = filterModules (rawSettings.modules-center or []); - modules-right = filterModules (rawSettings.modules-right or []); - }; - -in -{ - programs.waybar = { - # Gated on nomarchy.toggles.waybar — symmetric with services.hypridle - # (toggles.idle) and services.hyprsunset (toggles.nightlight). Disabled - # toggle means no waybar unit, so the bar stays hidden across rebuilds. - enable = lib.mkDefault config.nomarchy.toggles.waybar; - systemd.enable = lib.mkDefault config.nomarchy.toggles.waybar; - - settings = lib.mkDefault [ settings ]; - style = lib.mkDefault (builtins.readFile styleFile); - }; - - # Not mkDefault: home.packages is a list other modules set at normal - # priority, so a mkDefault def is dropped — leaving font-awesome (waybar's - # icon font) uninstalled. Merge it in. - home.packages = with pkgs; [ - font-awesome - ]; -} diff --git a/features/desktop/waybar/themes/summer-day/config.jsonc b/features/desktop/waybar/themes/summer-day/config.jsonc deleted file mode 100755 index f651bac..0000000 --- a/features/desktop/waybar/themes/summer-day/config.jsonc +++ /dev/null @@ -1,139 +0,0 @@ -{ - "margin-top": 0, - "margin-left": 120, - "margin-bottom": 0, - "margin-right": 120, - "height": 60, - "layer": "top", - "position": "top", - "spacing": 15, - "modules-left": ["custom/launcher", "clock", "clock#date"], - "modules-center": ["wlr/workspaces"], - "modules-right": ["custom/update", "pulseaudio", "network", "battery", "custom/powermenu"], - - "wlr/workspaces": { - "disable-scroll": true, - "all-outputs": true, - "on-click": "activate", - "on-scroll-up": "hyprctl dispatch workspace e+1", - "on-scroll-down": "hyprctl dispatch workspace e-1", - "persistent_workspaces": { - "1": [], - "2": [], - "3": [], - "4": [], - "5": [], - "6": [], - "7": [], - "8": [], - "9": [], - "10": [] - } - }, - - "custom/launcher": { - "interval": "once", - "format": "󰣇", - "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": { - "max-length": "4", - "format": "{icon}", - "tooltip-format": "{percent}%", - "format-icons": ["","","","", "", "", ""], - "on-click": "", - "on-scroll-up": "brightnessctl set 10%-", - "on-scroll-down": "brightnessctl set +10%" - }, - - "memory": { - "interval": 30, - "format": " {}%", - "format-alt":" {used:0.1f}G", - "max-length": 10 -}, - - "custom/dunst": { - "exec": "~/.config/waybar/scripts/dunst.sh", - "on-click": "dunstctl set-paused toggle", - "restart-interval": 1, - "tooltip": false - }, - - "pulseaudio": { - "format": "{icon} {volume}%", - "format-bluetooth": "{icon}  {volume}%", - "format-bluetooth-muted": "婢  muted", - "format-muted": "婢 muted", - "format-icons": { - "headphone": "", - "hands-free": "", - "headset": "", - "phone": "", - "portable": "", - "car": "", - "default": ["", "", ""] - }, - "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": "nomarchy-launch-wifi", - "tooltip-format": "Wifi controls\n\nSuper + Ctrl + W" -}, - -"battery": { - "bat": "BAT0", - "adapter": "ADP0", - "interval": 60, - "states": { - "warning": 30, - "critical": 15 - }, - "max-length": 10, - "format": "{icon} {capacity}%", - "format-warning": "{icon} {capacity}%", - "format-critical": "{icon} {capacity}%", - "format-charging": " {capacity}%", - "format-plugged": " {capacity}%", - "format-alt": "{icon} {capacity}%", - "format-full": " 100%", - "format-icons": ["", "", "", "", "", "", "", "", "", ""], - "tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B" -}, - -"clock": { - "format": " {:%H:%M}", - "tooltip": true, - "tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T" -}, - -"clock#date": { - "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": "nomarchy-menu power", - "tooltip": false - } -} diff --git a/features/desktop/waybar/themes/summer-night/config.jsonc b/features/desktop/waybar/themes/summer-night/config.jsonc deleted file mode 100644 index bbf82e2..0000000 --- a/features/desktop/waybar/themes/summer-night/config.jsonc +++ /dev/null @@ -1,156 +0,0 @@ -{ - "margin-top": 0, - "margin-left": 120, - "margin-bottom": 0, - "margin-right": 120, - "height": 61, - "layer": "top", - "position": "top", - "spacing": 15, - "modules-left": ["custom/nomarchy", "clock", "clock#date"], - "modules-center": ["hyprland/workspaces"], - "modules-right": ["custom/update", "idle_inhibitor", "pulseaudio", "custom/battery", "tray", "custom/powermenu"], - - "hyprland/workspaces": { - "disable-scroll": true, - "all-outputs": true, - "on-click": "activate", - "on-scroll-up": "hyprctl dispatch workspace r+1", - "on-scroll-down": "hyprctl dispatch workspace r-1", - "persistent_workspaces": { - "1": [], - "2": [], - "3": [], - "4": [], - "5": [], - "6": [], - "7": [], - "8": [], - "9": [], - "10": [] - } - }, - - "tray": { - "icon-size": 20, - "spacing": 5 - }, - - "custom/nomarchy": { - "interval": "once", - "format": "\uf000", - "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 - }, - - "custom/screenrecording-indicator": { - "on-click": "nomarchy-cmd-screenrecord", - "exec": "~/.config/nomarchy/default/waybar/indicators/screen-recording.sh", - "signal": 8, - "return-type": "json" - }, - - "custom/idle-indicator": { - "on-click": "nomarchy-toggle-idle", - "exec": "~/.config/nomarchy/default/waybar/indicators/idle.sh", - "signal": 9, - "return-type": "json" - }, - - "custom/notification-silencing-indicator": { - "on-click": "nomarchy-toggle-notification-silencing", - "exec": "~/.config/nomarchy/default/waybar/indicators/notification-silencing.sh", - "signal": 10, - "return-type": "json" - }, - - "backlight": { - "max-length": "4", - "format": "{icon} {percent}%", - "tooltip-format": "{percent}%", - "format-icons": ["󱩎","󱩏","󱩐","󱩑", "󱩓", "󱩔", "󰛨"], - "on-click": "", - "on-scroll-up": "brightnessctl set 10%-", - "on-scroll-down": "brightnessctl set +10%" - }, - - "memory": { - "interval": 30, - "format": " {}%", - "format-alt":" {used:0.1f}G", - "max-length": 10 -}, - - "idle_inhibitor": { - "format": "{icon}", - "format-icons": { - "activated": " 󰅶 ", - "deactivated": " 󰾪 " - } -}, - - "pulseaudio": { - "scroll-step": 5, - "format": "{icon} {volume}%", - "format-bluetooth": "{icon} {volume}%", - "format-bluetooth-muted": " {icon}", - "format-muted": " muted", - "format-icons": { - "headphone": "", - "hands-free": "", - "headset": "", - "phone": "", - "portable": "", - "car": "", - "default": ["", "", ""] - }, - "on-click": "pamixer -t", - "on-click-middle": "nomarchy-launch-audio", - "on-click-right": "pavucontrol", - "tooltip-format": "{icon} {desc} | {volume}%\n\nSuper + Ctrl + A" - }, - -"network": { - "format-wifi": " {signalStrength}%", - "format-ethernet": " {signalStrength}%", - "format-disconnected": "󰤭", - "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", - "tooltip-format": "Battery Status\n\nSuper + Ctrl + Alt + B" -}, - -"clock": { - "format": " {:%H:%M}", - "tooltip": true, - "tooltip-format": "Show time\n\nSuper + Ctrl + Alt + T" -}, - -"clock#date": { - "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": "nomarchy-menu power", - "tooltip": false - } -} diff --git a/features/scripts/battery-monitor.nix b/features/scripts/battery-monitor.nix deleted file mode 100644 index 035f989..0000000 --- a/features/scripts/battery-monitor.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ config, lib, pkgs, ... }: - -lib.mkIf (config.nomarchy.formFactor == "laptop") { - systemd.user.services.nomarchy-battery-monitor = { - Unit = { - Description = "Nomarchy Battery Monitor Check"; - After = [ "graphical-session.target" ]; - # Belt-and-braces: even on a laptop generation, skip if the kernel - # hasn't surfaced a battery yet (e.g. early boot, removable battery). - ConditionPathExistsGlob = "/sys/class/power_supply/BAT*"; - }; - - Service = { - Type = "oneshot"; - # The script is packaged in nomarchy-scripts which is in the home profile - ExecStart = "nomarchy-battery-monitor"; - Environment = [ "DISPLAY=:0" ]; - LogLevelMax = "warning"; - }; - }; - - systemd.user.timers.nomarchy-battery-monitor = { - Unit = { - Description = "Nomarchy Battery Monitor Timer"; - ConditionPathExistsGlob = "/sys/class/power_supply/BAT*"; - }; - - Timer = { - OnBootSec = "1min"; - OnUnitActiveSec = "30sec"; - AccuracySec = "10sec"; - }; - - Install = { - WantedBy = [ "timers.target" ]; - }; - }; -} diff --git a/features/scripts/default.nix b/features/scripts/default.nix deleted file mode 100644 index 5734c64..0000000 --- a/features/scripts/default.nix +++ /dev/null @@ -1,112 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - # Core dependencies needed by most scripts (minimal set) - coreDeps = with pkgs; [ - coreutils - gnused - gnugrep - findutils - gawk - jq - ]; - - # Category-specific dependencies - categoryDeps = with pkgs; { - appearance = [ - swww - libnotify - ]; - - apps = [ - hyprland - libnotify - ]; - - hardware = [ - brightnessctl - playerctl - pamixer - pciutils - libnotify - ]; - - system = [ - gum - libnotify - curl - wget - pkgs.home-manager - nixos-rebuild - ]; - - utils = [ - gum - glow - hyprland - libnotify - wl-clipboard - grim - slurp - xmlstarlet - curl - wget - ]; - - wm = [ - hyprland - swayosd - libnotify - procps - ]; - }; - - # All dependencies combined (for scripts that might call others) - allDeps = coreDeps ++ (lib.unique (lib.flatten (builtins.attrValues categoryDeps))); - - # Environment variables to inject - envVars = { - NOMARCHY_TOGGLE_SUSPEND = if config.nomarchy.toggles.suspend then "true" else "false"; - NOMARCHY_TOGGLE_SCREENSAVER = if config.nomarchy.toggles.screensaver then "true" else "false"; - NOMARCHY_TOGGLE_IDLE = if config.nomarchy.toggles.idle then "true" else "false"; - NOMARCHY_TOGGLE_NIGHTLIGHT = if config.nomarchy.toggles.nightlight then "true" else "false"; - NOMARCHY_TOGGLE_WAYBAR = if config.nomarchy.toggles.waybar then "true" else "false"; - NOMARCHY_MONOSPACE_FONT = config.nomarchy.fonts.monospace; - }; - - # Build wrapper arguments for environment variables - envWrapperArgs = lib.concatStringsSep " " ( - lib.mapAttrsToList (name: value: "--set ${name} \"${value}\"") envVars - ); - - nomarchy-scripts = pkgs.stdenv.mkDerivation { - pname = "nomarchy-scripts"; - version = "1.0.0"; - src = ./utils; - - nativeBuildInputs = [ pkgs.makeWrapper ]; - - installPhase = '' - mkdir -p $out/bin - cp * $out/bin/ - - chmod +x $out/bin/* - patchShebangs $out/bin - ''; - - postFixup = '' - # Wrap all scripts with all dependencies for robustness - deps="${lib.makeBinPath allDeps}" - for file in $out/bin/*; do - if [ -f "$file" ]; then - wrapProgram "$file" \ - --prefix PATH : "$deps" \ - ${envWrapperArgs} - fi - done - ''; - }; -in -{ - home.packages = [ nomarchy-scripts ]; -} diff --git a/features/scripts/utils/nomarchy-backup b/features/scripts/utils/nomarchy-backup deleted file mode 100755 index 76e1720..0000000 --- a/features/scripts/utils/nomarchy-backup +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Backup Script -# Alias for nomarchy-sync push. - -nomarchy-sync push "$@" diff --git a/features/scripts/utils/nomarchy-build-iso b/features/scripts/utils/nomarchy-build-iso deleted file mode 100755 index eb7880e..0000000 --- a/features/scripts/utils/nomarchy-build-iso +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Build the Nomarchy Installer ISO declaratively using the flake. - -echo "Building Nomarchy Installer ISO..." -nix build .#nixosConfigurations.nomarchy-installer.config.system.build.isoImage - -ISO_PATH=$(readlink -f result/iso/*.iso) -echo "Success! ISO built at: $ISO_PATH" -echo "You can now burn this to a USB drive using 'dd' or 'etcher'." diff --git a/features/scripts/utils/nomarchy-build-live-iso b/features/scripts/utils/nomarchy-build-live-iso deleted file mode 100755 index a23df29..0000000 --- a/features/scripts/utils/nomarchy-build-live-iso +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Build the Nomarchy Live ISO (Full Desktop Environment) using the flake. - -echo "Building Nomarchy Live ISO..." -nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage - -ISO_PATH=$(readlink -f result/iso/*.iso) -echo "Success! Live ISO built at: $ISO_PATH" -echo "You can now burn this to a USB drive using 'dd' or 'etcher'." diff --git a/features/scripts/utils/nomarchy-cmd-audio-switch b/features/scripts/utils/nomarchy-cmd-audio-switch deleted file mode 100755 index f964f08..0000000 --- a/features/scripts/utils/nomarchy-cmd-audio-switch +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash -set -e - -# Switch between audio outputs while preserving the mute status. By default mapped to Super + Mute. - -focused_monitor="$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name')" - -sinks=$(pactl -f json list sinks | jq '[.[] | select((.ports | length == 0) or ([.ports[]? | .availability != "not available"] | any))]') -sinks_count=$(echo "$sinks" | jq '. | length') - -if (( sinks_count == 0 )); then - swayosd-client \ - --monitor "$focused_monitor" \ - --custom-message "No audio devices found" - exit 1 -fi - -current_sink_name=$(pactl get-default-sink) -current_sink_index=$(echo "$sinks" | jq -r --arg name "$current_sink_name" 'map(.name) | index($name)') - -if [[ $current_sink_index != "null" ]]; then - next_sink_index=$(((current_sink_index + 1) % sinks_count)) -else - next_sink_index=0 -fi - -next_sink=$(echo "$sinks" | jq -r ".[$next_sink_index]") -next_sink_name=$(echo "$next_sink" | jq -r '.name') - -next_sink_description=$(echo "$next_sink" | jq -r '.description') -if [[ $next_sink_description == "(null)" ]] || [[ $next_sink_description == "null" ]] || [[ -z $next_sink_description ]]; then - # For Bluetooth devices, the friendly name is on the Device entry (device.id), not the Sink entry (object.id) - device_id=$(echo "$next_sink" | jq -r '.properties."device.id"') - if [[ $device_id != "null" ]] && [[ -n $device_id ]]; then - next_sink_description=$(wpctl status | grep -E "^\s*│?\s+${device_id}\." | sed -E 's/^.*[0-9]+\.\s+//' | sed -E 's/\s+\[.*$//') - fi - # Fall back to object.id lookup if device.id didn't yield a result - if [[ -z $next_sink_description ]]; then - sink_id=$(echo "$next_sink" | jq -r '.properties."object.id"') - next_sink_description=$(wpctl status | grep -E "\s+\*?\s+${sink_id}\." | sed -E 's/^.*[0-9]+\.\s+//' | sed -E 's/\s+\[.*$//') - fi -fi - -next_sink_volume=$(echo "$next_sink" | jq -r \ - '.volume | to_entries[0].value.value_percent | sub("%"; "")') -next_sink_is_muted=$(echo "$next_sink" | jq -r '.mute') - -if [[ $next_sink_is_muted = "true" ]] || (( next_sink_volume == 0 )); then - icon_state="muted" -elif (( next_sink_volume <= 33 )); then - icon_state="low" -elif (( next_sink_volume <= 66 )); then - icon_state="medium" -else - icon_state="high" -fi - -next_sink_volume_icon="sink-volume-${icon_state}-symbolic" - -if [[ $next_sink_name != $current_sink_name ]]; then - pactl set-default-sink "$next_sink_name" -fi - -swayosd-client \ - --monitor "$focused_monitor" \ - --custom-message "$next_sink_description" \ - --custom-icon "$next_sink_volume_icon" diff --git a/features/scripts/utils/nomarchy-cmd-present b/features/scripts/utils/nomarchy-cmd-present deleted file mode 100755 index 0c39756..0000000 --- a/features/scripts/utils/nomarchy-cmd-present +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -# Returns true if all the commands passed in as arguments exit on the system. - -for cmd in "$@"; do - command -v "$cmd" &>/dev/null || exit 1 -done - -exit 0 diff --git a/features/scripts/utils/nomarchy-cmd-screenrecord b/features/scripts/utils/nomarchy-cmd-screenrecord deleted file mode 100755 index edd9faf..0000000 --- a/features/scripts/utils/nomarchy-cmd-screenrecord +++ /dev/null @@ -1,185 +0,0 @@ -#!/bin/bash -set -e - -# Start and stop a screenrecording, which will be saved to ~/Videos by default. -# Alternative location can be set via NOMARCHY_SCREENRECORD_DIR or XDG_VIDEOS_DIR ENVs. -# Resolution is capped to 4K for monitors above 4K, native otherwise. -# Override via --resolution= (e.g. --resolution=1920x1080, --resolution=0x0 for native). - -[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs -OUTPUT_DIR="${NOMARCHY_SCREENRECORD_DIR:-${XDG_VIDEOS_DIR:-$HOME/Videos}}" - -if [[ ! -d $OUTPUT_DIR ]]; then - notify-send "Screen recording directory does not exist: $OUTPUT_DIR" -u critical -t 3000 - exit 1 -fi - -DESKTOP_AUDIO="false" -MICROPHONE_AUDIO="false" -WEBCAM="false" -WEBCAM_DEVICE="" -RESOLUTION="" -STOP_RECORDING="false" -RECORDING_FILE="/tmp/nomarchy-screenrecord-filename" - -for arg in "$@"; do - case "$arg" in - --with-desktop-audio) DESKTOP_AUDIO="true" ;; - --with-microphone-audio) MICROPHONE_AUDIO="true" ;; - --with-webcam) WEBCAM="true" ;; - --webcam-device=*) WEBCAM_DEVICE="${arg#*=}" ;; - --resolution=*) RESOLUTION="${arg#*=}" ;; - --stop-recording) STOP_RECORDING="true" ;; - esac -done - -start_webcam_overlay() { - cleanup_webcam - - # Auto-detect first available webcam if none specified - if [[ -z $WEBCAM_DEVICE ]]; then - WEBCAM_DEVICE=$(v4l2-ctl --list-devices 2>/dev/null | grep -m1 "^[[:space:]]*/dev/video" | tr -d '\t') - if [[ -z $WEBCAM_DEVICE ]]; then - notify-send "No webcam devices found" -u critical -t 3000 - return 1 - fi - fi - - # Get monitor scale - local scale=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .scale') - - # Target width (base 360px, scaled to monitor) - local target_width=$(awk "BEGIN {printf \"%.0f\", 360 * $scale}") - - # Try preferred 16:9 resolutions in order, use first available - local preferred_resolutions=("640x360" "1280x720" "1920x1080") - local video_size_arg="" - local available_formats=$(v4l2-ctl --list-formats-ext -d "$WEBCAM_DEVICE" 2>/dev/null) - - for resolution in "${preferred_resolutions[@]}"; do - if echo "$available_formats" | grep -q "$resolution"; then - video_size_arg="-video_size $resolution" - break - fi - done - - ffplay -f v4l2 $video_size_arg -framerate 30 "$WEBCAM_DEVICE" \ - -vf "crop=iw/2:ih,scale=${target_width}:-1" \ - -window_title "WebcamOverlay" \ - -noborder \ - -fflags nobuffer -flags low_delay \ - -probesize 32 -analyzeduration 0 \ - -loglevel quiet & - sleep 1 -} - -cleanup_webcam() { - pkill -f "WebcamOverlay" 2>/dev/null -} - -default_resolution() { - local width height - read -r width height < <(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | "\(.width) \(.height)"') - if ((width > 3840 || height > 2160)); then - echo "3840x2160" - else - echo "0x0" - fi -} - -start_screenrecording() { - local filename="$OUTPUT_DIR/screenrecording-$(date +'%Y-%m-%d_%H-%M-%S').mp4" - local audio_devices="" - local audio_args=() - - [[ $DESKTOP_AUDIO == "true" ]] && audio_devices+="default_output" - - if [[ $MICROPHONE_AUDIO == "true" ]]; then - # Merge audio tracks into one - separate tracks only play one at a time in most players - [[ -n $audio_devices ]] && audio_devices+="|" - audio_devices+="default_input" - fi - - [[ -n $audio_devices ]] && audio_args+=(-a "$audio_devices" -ac aac) - - local resolution="${RESOLUTION:-$(default_resolution)}" - - gpu-screen-recorder -w portal -k auto -s "$resolution" -f 60 -fm cfr -fallback-cpu-encoding yes -o "$filename" "${audio_args[@]}" & - local pid=$! - - # Wait for recording to actually start (file appears after portal selection) - while kill -0 $pid 2>/dev/null && [[ ! -f $filename ]]; do - sleep 0.2 - done - - if kill -0 $pid 2>/dev/null; then - echo "$filename" >"$RECORDING_FILE" - toggle_screenrecording_indicator - fi -} - -stop_screenrecording() { - pkill -SIGINT -f "^gpu-screen-recorder" # SIGINT required to save video properly - - # Wait a maximum of 5 seconds to finish before hard killing - local count=0 - while pgrep -f "^gpu-screen-recorder" >/dev/null && ((count < 50)); do - sleep 0.1 - count=$((count + 1)) - done - - toggle_screenrecording_indicator - cleanup_webcam - - if pgrep -f "^gpu-screen-recorder" >/dev/null; then - pkill -9 -f "^gpu-screen-recorder" - notify-send "Screen recording error" "Recording process had to be force-killed. Video may be corrupted." -u critical -t 5000 - else - trim_first_frame - local filename=$(cat "$RECORDING_FILE" 2>/dev/null) - local preview="${filename%.mp4}-preview.png" - - # Generate a preview thumbnail from the first frame - ffmpeg -y -i "$filename" -ss 00:00:00.1 -vframes 1 -q:v 2 "$preview" -loglevel quiet 2>/dev/null - - ( - ACTION=$(notify-send "Screen recording saved" "Open with Super + Alt + , (or click this)" -t 10000 -i "${preview:-$filename}" -A "default=open") - [[ $ACTION == "default" ]] && mpv "$filename" - rm -f "$preview" - ) & - fi - - rm -f "$RECORDING_FILE" -} - -toggle_screenrecording_indicator() { - pkill -RTMIN+8 waybar -} - -screenrecording_active() { - pgrep -f "^gpu-screen-recorder" >/dev/null -} - -trim_first_frame() { - local latest - latest=$(cat "$RECORDING_FILE" 2>/dev/null) - - if [[ -n $latest && -f $latest ]]; then - local trimmed="${latest%.mp4}-trimmed.mp4" - if ffmpeg -y -ss 0.1 -i "$latest" -c copy "$trimmed" -loglevel quiet 2>/dev/null; then - mv "$trimmed" "$latest" - else - rm -f "$trimmed" - fi - fi -} - -if screenrecording_active; then - stop_screenrecording -elif [[ $STOP_RECORDING == "true" ]]; then - exit 1 -else - [[ $WEBCAM == "true" ]] && start_webcam_overlay - - start_screenrecording || cleanup_webcam -fi diff --git a/features/scripts/utils/nomarchy-cmd-screensaver b/features/scripts/utils/nomarchy-cmd-screensaver deleted file mode 100755 index 033b89f..0000000 --- a/features/scripts/utils/nomarchy-cmd-screensaver +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -set -e - -# Run the Nomarchy screensaver using random effects from TTE. - -screensaver_in_focus() { - hyprctl activewindow -j | jq -e '.class == "org.nomarchy.screensaver"' >/dev/null 2>&1 -} - -exit_screensaver() { - hyprctl keyword cursor:invisible false &>/dev/null || true - pkill -x tte 2>/dev/null - pkill -f org.nomarchy.screensaver 2>/dev/null - exit 0 -} - -# Exit the screensaver on signals and input from keyboard and mouse -trap exit_screensaver SIGINT SIGTERM SIGHUP SIGQUIT - -printf '\033]11;rgb:00/00/00\007' # Set background color to black - -hyprctl keyword cursor:invisible true &>/dev/null - -tty=$(tty 2>/dev/null) - -while true; do - tte -i ~/.config/nomarchy/branding/logo.txt \ - --frame-rate 120 --canvas-width 0 --canvas-height 0 --reuse-canvas --anchor-canvas c --anchor-text c\ - --random-effect --exclude-effects dev_worm \ - --no-eol --no-restore-cursor & - - while pgrep -t "${tty#/dev/}" -x tte >/dev/null; do - if read -n1 -t 1 || ! screensaver_in_focus; then - exit_screensaver - fi - done -done diff --git a/features/scripts/utils/nomarchy-cmd-screenshot b/features/scripts/utils/nomarchy-cmd-screenshot deleted file mode 100755 index dc9e2bc..0000000 --- a/features/scripts/utils/nomarchy-cmd-screenshot +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/bash -set -e - -# Take a screenshot of the whole screen, a specific window, or a user-drawn region. -# Saves to ~/Pictures by default, but that can be changed via NOMARCHY_SCREENSHOT_DIR or XDG_PICTURES_DIR ENVs. -# Editor defaults to Satty but can be changed via --editor=<name> or NOMARCHY_SCREENSHOT_EDITOR env - -[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs -OUTPUT_DIR="${NOMARCHY_SCREENSHOT_DIR:-${XDG_PICTURES_DIR:-$HOME/Pictures}}" - -if [[ ! -d $OUTPUT_DIR ]]; then - notify-send "Screenshot directory does not exist: $OUTPUT_DIR" -u critical -t 3000 - exit 1 -fi - -pkill slurp && exit 0 - -SCREENSHOT_EDITOR="${NOMARCHY_SCREENSHOT_EDITOR:-satty}" - -# Parse --editor flag from any position -ARGS=() -for arg in "$@"; do - if [[ $arg == --editor=* ]]; then - SCREENSHOT_EDITOR="${arg#--editor=}" - else - ARGS+=("$arg") - fi -done -set -- "${ARGS[@]}" - -open_editor() { - local filepath="$1" - if [[ $SCREENSHOT_EDITOR == "satty" ]]; then - satty --filename "$filepath" \ - --output-filename "$filepath" \ - --actions-on-enter save-to-clipboard \ - --save-after-copy \ - --copy-command 'wl-copy' - else - $SCREENSHOT_EDITOR "$filepath" - fi -} - -MODE="${1:-smart}" -PROCESSING="${2:-slurp}" - -# accounting for portrait/transformed displays -JQ_MONITOR_GEO=' - def format_geo: - .x as $x | .y as $y | - (.width / .scale | floor) as $w | - (.height / .scale | floor) as $h | - .transform as $t | - if $t == 1 or $t == 3 then - "\($x),\($y) \($h)x\($w)" - else - "\($x),\($y) \($w)x\($h)" - end; -' - -get_rectangles() { - local active_workspace=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .activeWorkspace.id') - hyprctl monitors -j | jq -r --arg ws "$active_workspace" "${JQ_MONITOR_GEO} .[] | select(.activeWorkspace.id == (\$ws | tonumber)) | format_geo" - hyprctl clients -j | jq -r --arg ws "$active_workspace" '.[] | select(.workspace.id == ($ws | tonumber)) | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' -} - -# Select based on mode -case "$MODE" in -region) - hyprpicker -r -z >/dev/null 2>&1 & - PID=$! - sleep .1 - SELECTION=$(slurp 2>/dev/null) - kill $PID 2>/dev/null - ;; -windows) - hyprpicker -r -z >/dev/null 2>&1 & - PID=$! - sleep .1 - SELECTION=$(get_rectangles | slurp -r 2>/dev/null) - kill $PID 2>/dev/null - ;; -fullscreen) - SELECTION=$(hyprctl monitors -j | jq -r "${JQ_MONITOR_GEO} .[] | select(.focused == true) | format_geo") - ;; -smart | *) - RECTS=$(get_rectangles) - hyprpicker -r -z >/dev/null 2>&1 & - PID=$! - sleep .1 - SELECTION=$(echo "$RECTS" | slurp 2>/dev/null) - kill $PID 2>/dev/null - - # If the selection area is L * W < 20, we'll assume you were trying to select whichever - # window or output it was inside of to prevent accidental 2px snapshots - if [[ $SELECTION =~ ^([0-9]+),([0-9]+)[[:space:]]([0-9]+)x([0-9]+)$ ]]; then - if ((${BASH_REMATCH[3]} * ${BASH_REMATCH[4]} < 20)); then - click_x="${BASH_REMATCH[1]}" - click_y="${BASH_REMATCH[2]}" - - while IFS= read -r rect; do - if [[ $rect =~ ^([0-9]+),([0-9]+)[[:space:]]([0-9]+)x([0-9]+) ]]; then - rect_x="${BASH_REMATCH[1]}" - rect_y="${BASH_REMATCH[2]}" - rect_width="${BASH_REMATCH[3]}" - rect_height="${BASH_REMATCH[4]}" - - if ((click_x >= rect_x && click_x < rect_x + rect_width && click_y >= rect_y && click_y < rect_y + rect_height)); then - SELECTION="${rect_x},${rect_y} ${rect_width}x${rect_height}" - break - fi - fi - done <<<"$RECTS" - fi - fi - ;; -esac - -[[ -z $SELECTION ]] && exit 0 - -FILENAME="screenshot-$(date +'%Y-%m-%d_%H-%M-%S').png" -FILEPATH="$OUTPUT_DIR/$FILENAME" - -if [[ $PROCESSING == "slurp" ]]; then - grim -g "$SELECTION" "$FILEPATH" || exit 1 - wl-copy <"$FILEPATH" - - ( - ACTION=$(notify-send "Screenshot saved to clipboard and file" "Edit with Super + Alt + , (or click this)" -t 10000 -i "$FILEPATH" -A "default=edit") - [[ $ACTION == "default" ]] && open_editor "$FILEPATH" - ) & -else - grim -g "$SELECTION" - | wl-copy -fi diff --git a/features/scripts/utils/nomarchy-cmd-share b/features/scripts/utils/nomarchy-cmd-share deleted file mode 100755 index 804fb65..0000000 --- a/features/scripts/utils/nomarchy-cmd-share +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -set -e - -# Share clipboard, file, or folder using LocalSend. Bound to Super + Ctrl + S by default. - -if (($# == 0)); then - echo "Usage: nomarchy-cmd-share [clipboard|file|folder]" - exit 1 -fi - -MODE="$1" -shift - -if [[ $MODE == "clipboard" ]]; then - TEMP_FILE=$(mktemp --suffix=.txt) - wl-paste >"$TEMP_FILE" - FILES="$TEMP_FILE" -else - if (($# > 0)); then - FILES="$*" - else - if [[ $MODE == "folder" ]]; then - # Pick a single folder from home directory - FILES=$(find "$HOME" -type d 2>/dev/null | fzf) - else - # Pick one or more files from home directory - FILES=$(find "$HOME" -type f 2>/dev/null | fzf --multi) - fi - [[ -z $FILES ]] && exit 0 - fi -fi - -# Run LocalSend in its own systemd service (detached from terminal) -# Convert newline-separated files to space-separated arguments -if [[ $MODE != "clipboard" ]] && echo "$FILES" | grep -q $'\n'; then - # Multiple files selected - convert newlines to array - readarray -t FILE_ARRAY <<<"$FILES" - systemd-run --user --quiet --collect localsend --headless send "${FILE_ARRAY[@]}" -else - # Single file or clipboard mode - systemd-run --user --quiet --collect localsend --headless send "$FILES" -fi - -# Note: Temporary file will remain until system cleanup for clipboard mode -# This ensures the file content is available for the LocalSend GUI - -exit 0 diff --git a/features/scripts/utils/nomarchy-cmd-terminal-cwd b/features/scripts/utils/nomarchy-cmd-terminal-cwd deleted file mode 100755 index cf9a6e4..0000000 --- a/features/scripts/utils/nomarchy-cmd-terminal-cwd +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -set -e - -# Returns the current working directory of the active terminal window, -# so a new terminal window can be started in the same directory. - -# Go from current active terminal to its child shell process and run cwd there -terminal_pid=$(hyprctl activewindow | awk '/pid:/ {print $2}') -shell_pid=$(pgrep -P "$terminal_pid" | tail -n1) - -if [[ -n $shell_pid ]]; then - cwd=$(readlink -f "/proc/$shell_pid/cwd" 2>/dev/null) - shell=$(readlink -f "/proc/$shell_pid/exe" 2>/dev/null) - - # Check if $shell is a valid shell and $cwd is a directory. - if grep -qs "$shell" /etc/shells && [[ -d $cwd ]]; then - echo "$cwd" - else - echo "$HOME" - fi -else - echo "$HOME" -fi diff --git a/features/scripts/utils/nomarchy-debug b/features/scripts/utils/nomarchy-debug deleted file mode 100755 index 86cda64..0000000 --- a/features/scripts/utils/nomarchy-debug +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy Debug Information Script -# Collects system information for troubleshooting. - -set -e - -# Flags for agent use to avoid hanging -PRINT_ONLY=false -if [[ "$*" == *"--print"* ]]; then - PRINT_ONLY=true -fi - -echo "--- Nomarchy Debug Info ---" -echo "Version: $(nomarchy-version)" -echo "Host: $(hostname)" -echo "Kernel: $(uname -r)" -echo "Uptime: $(uptime -p)" - -echo "" -echo "--- Graphics ---" -if command -v hyprctl &>/dev/null; then - hyprctl version | head -n 1 -fi -if command -v glxinfo &>/dev/null; then - glxinfo | grep "OpenGL renderer" || echo "OpenGL: info not available" -fi - -echo "" -echo "--- Nix/NixOS ---" -nix --version -if [[ -f /etc/os-release ]]; then - grep "PRETTY_NAME" /etc/os-release | cut -d'"' -f2 -fi - -echo "" -echo "--- Nomarchy State ---" -STATE_FILE="$HOME/.config/nomarchy/state.json" -if [[ -f "$STATE_FILE" ]]; then - jq '.' "$STATE_FILE" -else - echo "State file not found at $STATE_FILE" -fi - -echo "" -echo "--- Services Status ---" -for svc in waybar hypridle hyprlock walker fprintd fwupd; do - if systemctl is-active --quiet "$svc" 2>/dev/null || systemctl --user is-active --quiet "$svc" 2>/dev/null; then - echo "[ACTIVE] $svc" - else - echo "[INACTIVE] $svc" - fi -done - -echo "" -echo "--- End of Debug Info ---" - -if [[ "$PRINT_ONLY" == "false" ]]; then - echo "" - echo "Would you like to upload this log to a pastebin? (y/N)" - read -r answer - if [[ "$answer" =~ ^[Yy]$ ]]; then - if command -v nomarchy-upload-log &>/dev/null; then - nomarchy-debug --print | nomarchy-upload-log - else - echo "Error: nomarchy-upload-log not found." - fi - fi -fi diff --git a/features/scripts/utils/nomarchy-drive-info b/features/scripts/utils/nomarchy-drive-info deleted file mode 100755 index ec9e2dc..0000000 --- a/features/scripts/utils/nomarchy-drive-info +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -set -e - -# Returns drive information about a given volumne, like /dev/nvme0, which is used by nomarchy-drive-select. - -if (($# == 0)); then - echo "Usage: nomarchy-drive-info [/dev/drive]" - exit 1 -else - drive="$1" -fi - -# Find the root drive in case we are looking at partitions -root_drive=$(lsblk -no PKNAME "$drive" 2>/dev/null | tail -n1) -if [[ -n $root_drive ]]; then - root_drive="/dev/$root_drive" -else - root_drive="$drive" -fi - -# Get basic disk information -size=$(lsblk -dno SIZE "$drive" 2>/dev/null) -vendor=$(lsblk -dno VENDOR "$root_drive" 2>/dev/null | sed 's/ *$//') -model=$(lsblk -dno MODEL "$root_drive" 2>/dev/null | sed 's/ *$//') - -# Combine vendor and model, avoiding duplication -label="" -if [[ -n $vendor && -n $model ]]; then - if [[ $model == *$vendor* ]]; then - label="$model" - else - label="$vendor $model" - fi -elif [[ -n $model ]]; then - label="$model" -elif [[ -n $vendor ]]; then - label="$vendor" -fi - -# Format display string -display="$drive" -[[ -n $size ]] && display="$display ($size)" -[[ -n $label ]] && display="$display - $label" - -# Append compact partition summary -part_summary=$(lsblk -nro TYPE,NAME,FSTYPE,MOUNTPOINT "$root_drive" 2>/dev/null | \ - awk '$1=="part" { printf "%s%s%s", s, ($3==""?"unknown":$3), ($4==""?"":"("$4")"); s=", " }') -[[ -n $part_summary ]] && display+=" [$part_summary]" - -echo "$display" diff --git a/features/scripts/utils/nomarchy-drive-select b/features/scripts/utils/nomarchy-drive-select deleted file mode 100755 index 8c7d993..0000000 --- a/features/scripts/utils/nomarchy-drive-select +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e - -# Select a drive from a list with info that includes space and brand. Used by nomarchy-drive-set-password. - -if (($# == 0)); then - drives=$(lsblk -dpno NAME | grep -E '/dev/(sd|hd|vd|nvme|mmcblk|xv)') -else - drives="$@" -fi - -drives_with_info="" -while IFS= read -r drive; do - [[ -n $drive ]] || continue - drives_with_info+="$(nomarchy-drive-info "$drive")"$'\n' -done <<<"$drives" - -selected_drive="$(printf "%s" "$drives_with_info" | gum choose --header "Select drive")" || exit 1 -printf "%s\n" "$selected_drive" | awk '{print $1}' diff --git a/features/scripts/utils/nomarchy-drive-set-password b/features/scripts/utils/nomarchy-drive-set-password deleted file mode 100755 index a55da49..0000000 --- a/features/scripts/utils/nomarchy-drive-set-password +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -e - -# Set a new encryption password for a drive selected. - -encrypted_drives=$(blkid -t TYPE=crypto_LUKS -o device) - -if [[ -n $encrypted_drives ]]; then - if (( $(wc -l <<<encrypted_drives) == 1 )); then - drive_to_change="$encrypted_drives" - else - drive_to_change="$(nomarchy-drive-select "$encrypted_drives")" - fi - - if [[ -n $drive_to_change ]]; then - echo "Changing full-disk encryption password for $drive_to_change" - sudo cryptsetup luksChangeKey --pbkdf argon2id --iter-time 2000 "$drive_to_change" - else - echo "No drive selected." - fi -else - echo "No encrypted drives available." - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-env-update b/features/scripts/utils/nomarchy-env-update deleted file mode 100755 index 55e0cdc..0000000 --- a/features/scripts/utils/nomarchy-env-update +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy Environment Update Script -# Standalone Home Manager iteration path. Use this for fast dotfile and -# theme changes that don't need a full system rebuild. For first-boot -# dotfiles and any system-level change, the NixOS module activation -# from `sudo nixos-rebuild switch` is the source of truth. - -set -e - -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 - -if command -v nomarchy-preflight-migration >/dev/null 2>&1; then - nomarchy-preflight-migration -fi - -echo "Applying user-level changes from $REPO_DIR#$USER..." -if command -v home-manager >/dev/null 2>&1; then - home-manager switch --flake "$REPO_DIR#$USER" --impure -else - # Bootstrap path: HM hasn't put `home-manager` on PATH yet (e.g. running - # straight after a partial install). Pull it from the flake registry. - nix --extra-experimental-features 'nix-command flakes' \ - run 'home-manager/release-25.11' \ - -- switch --flake "$REPO_DIR#$USER" --impure -fi - -echo "Environment update complete." -nomarchy-hook post-update diff --git a/features/scripts/utils/nomarchy-font b/features/scripts/utils/nomarchy-font deleted file mode 100755 index 6bdf84e..0000000 --- a/features/scripts/utils/nomarchy-font +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Font Helper -# Usage: nomarchy-font [selector|set <name>|list] - -COMMAND="$1" - -case "$COMMAND" in - set) - shift - nomarchy-font-set "$@" - ;; - list) - nomarchy-font-list - ;; - selector|*) - theme=$(echo -e "$(nomarchy-font-list)" | nomarchy-launch-walker --dmenu --width 350 -p "Font...") - if [[ -n "$theme" && "$theme" != "CNCLD" ]]; then - nomarchy-font-set "$theme" - fi - ;; -esac diff --git a/features/scripts/utils/nomarchy-hook b/features/scripts/utils/nomarchy-hook deleted file mode 100755 index 7f4db73..0000000 --- a/features/scripts/utils/nomarchy-hook +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Run a named hook, like post-update (available in ~/.config/nomarchy/hooks/post-update). - -set -e - -if (( $# < 1 )); then - echo "Usage: nomarchy-hook [name] [args...]" - exit 1 -fi - -HOOK=$1 -HOOK_PATH="$HOME/.config/nomarchy/hooks/$1" -shift - -if [[ -f $HOOK_PATH ]]; then - bash "$HOOK_PATH" "$@" -fi diff --git a/features/scripts/utils/nomarchy-hyprland-active-window-transparency-toggle b/features/scripts/utils/nomarchy-hyprland-active-window-transparency-toggle deleted file mode 100755 index 59f492e..0000000 --- a/features/scripts/utils/nomarchy-hyprland-active-window-transparency-toggle +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Toggles transparency for the currently focused window. - -hyprctl dispatch setprop "address:$(hyprctl activewindow -j | jq -r '.address')" opaque toggle diff --git a/features/scripts/utils/nomarchy-hyprland-monitor-scaling-cycle b/features/scripts/utils/nomarchy-hyprland-monitor-scaling-cycle deleted file mode 100755 index 755e09a..0000000 --- a/features/scripts/utils/nomarchy-hyprland-monitor-scaling-cycle +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -e - -# Get the active monitor (the one with the cursor) -MONITOR_INFO=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)') -ACTIVE_MONITOR=$(echo "$MONITOR_INFO" | jq -r '.name') -CURRENT_SCALE=$(echo "$MONITOR_INFO" | jq -r '.scale') -WIDTH=$(echo "$MONITOR_INFO" | jq -r '.width') -HEIGHT=$(echo "$MONITOR_INFO" | jq -r '.height') -REFRESH_RATE=$(echo "$MONITOR_INFO" | jq -r '.refreshRate') - -# Cycle through scales: 1 → 1.6 → 2 → 3 → 1 -CURRENT_INT=$(awk -v s="$CURRENT_SCALE" 'BEGIN { printf "%.0f", s * 10 }') - -case "$CURRENT_INT" in -10) NEW_SCALE=1.6 ;; -16) NEW_SCALE=2 ;; -20) NEW_SCALE=3 ;; -*) NEW_SCALE=1 ;; -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" diff --git a/features/scripts/utils/nomarchy-hyprland-window-close-all b/features/scripts/utils/nomarchy-hyprland-window-close-all deleted file mode 100755 index c0f7914..0000000 --- a/features/scripts/utils/nomarchy-hyprland-window-close-all +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -# Close all open windows -hyprctl clients -j | \ - jq -r ".[].address" | \ - xargs -I{} hyprctl dispatch closewindow address:{} - -# Move to first workspace -hyprctl dispatch workspace 1 diff --git a/features/scripts/utils/nomarchy-hyprland-window-gaps-toggle b/features/scripts/utils/nomarchy-hyprland-window-gaps-toggle deleted file mode 100755 index 790d3e2..0000000 --- a/features/scripts/utils/nomarchy-hyprland-window-gaps-toggle +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Toggles the window gaps globally between no gaps and the default 10/5/2, declaratively and instantly. - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" -mkdir -p "$STATE_DIR" - -if [ ! -f "$STATE_FILE" ]; then - echo "{}" > "$STATE_FILE" -fi - -gaps=$(jq -r '.hyprland.gaps_out // 10' "$STATE_FILE") - -if [[ $gaps == "0" ]]; then - NEW_STATE='{"gaps_out": 10, "gaps_in": 5, "border_size": 2}' - hyprctl keyword general:gaps_out 10 - hyprctl keyword general:gaps_in 5 - hyprctl keyword general:border_size 2 -else - NEW_STATE='{"gaps_out": 0, "gaps_in": 0, "border_size": 0}' - hyprctl keyword general:gaps_out 0 - hyprctl keyword general:gaps_in 0 - hyprctl keyword general:border_size 0 -fi - -nomarchy-state-write hyprland "$NEW_STATE" --type json - -echo "Toggled gaps to $NEW_STATE declaratively." diff --git a/features/scripts/utils/nomarchy-hyprland-window-pop b/features/scripts/utils/nomarchy-hyprland-window-pop deleted file mode 100755 index 05ef745..0000000 --- a/features/scripts/utils/nomarchy-hyprland-window-pop +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -set -e - -# Toggle to pop-out a tile to stay fixed on a display basis. - -# Usage: -# nomarchy-hyprland-window-pop [width height [x y]] -# -# Arguments: -# width Optional. Width of the floating window. Default: 1300 -# height Optional. Height of the floating window. Default: 900 -# x Optional. X position of the window. Must provide both X and Y to take effect. -# y Optional. Y position of the window. Must provide both X and Y to take effect. -# -# Behavior: -# - If the window is already pinned, it will be unpinned and removed from the pop layer. -# - If the window is not pinned, it will be floated, resized, moved/centered, pinned, brought to top, and popped. - -width=${1:-1300} -height=${2:-900} -x=${3:-} -y=${4:-} - -active=$(hyprctl activewindow -j) -pinned=$(echo "$active" | jq ".pinned") -addr=$(echo "$active" | jq -r ".address") - -if [[ $pinned == "true" ]]; then - hyprctl -q --batch \ - "dispatch pin address:$addr;" \ - "dispatch togglefloating address:$addr;" \ - "dispatch tagwindow -pop address:$addr;" -elif [[ -n $addr ]]; then - hyprctl dispatch togglefloating address:$addr - hyprctl dispatch resizeactive exact $width $height address:$addr - - if [[ -n $x && -n $y ]]; then - hyprctl dispatch moveactive $x $y address:$addr - else - hyprctl dispatch centerwindow address:$addr - fi - - hyprctl -q --batch \ - "dispatch pin address:$addr;" \ - "dispatch alterzorder top address:$addr;" \ - "dispatch tagwindow +pop address:$addr;" -fi diff --git a/features/scripts/utils/nomarchy-hyprland-window-single-square-aspect-toggle b/features/scripts/utils/nomarchy-hyprland-window-single-square-aspect-toggle deleted file mode 100755 index 0594ac4..0000000 --- a/features/scripts/utils/nomarchy-hyprland-window-single-square-aspect-toggle +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -# single_window_aspect_ratio lives under the dwindle namespace (the default -# layout), not a bare `layout:` — `hyprctl getoption layout:…` returns -# "no such option" and the keyword set is silently dropped. -CURRENT_VALUE=$(hyprctl getoption "dwindle:single_window_aspect_ratio" 2>/dev/null | head -1) - -# Parse vec2 output: "vec2: [1, 1]" or "vec2: [0, 0]" -if [[ $CURRENT_VALUE == *"[1, 1]"* ]]; then - hyprctl keyword dwindle:single_window_aspect_ratio "0 0" - notify-send -u low " Disable single-window square aspect ratio" -else - hyprctl keyword dwindle:single_window_aspect_ratio "1 1" - notify-send -u low " Enable single-window square aspect" -fi diff --git a/features/scripts/utils/nomarchy-hyprland-workspace-layout-toggle b/features/scripts/utils/nomarchy-hyprland-workspace-layout-toggle deleted file mode 100755 index 2070190..0000000 --- a/features/scripts/utils/nomarchy-hyprland-workspace-layout-toggle +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e - -# Toggle the tiling layout between Hyprland's two built-in layouts, dwindle -# and master. (The old version read `.tiledLayout` off `hyprctl -# activeworkspace -j` and switched to "scrolling" — but that field doesn't -# exist, so the read was always null, and "scrolling" isn't a real layout -# (`hyprctl layouts` only lists dwindle/master), so the toggle silently -# did nothing.) - -CURRENT_LAYOUT=$(hyprctl getoption general:layout -j | jq -r '.str') - -case "$CURRENT_LAYOUT" in - dwindle) NEW_LAYOUT=master ;; - *) NEW_LAYOUT=dwindle ;; -esac - -hyprctl keyword general:layout "$NEW_LAYOUT" -notify-send -u low "󱂬 Workspace layout set to $NEW_LAYOUT" diff --git a/features/scripts/utils/nomarchy-install b/features/scripts/utils/nomarchy-install deleted file mode 100755 index 2e95367..0000000 --- a/features/scripts/utils/nomarchy-install +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Install Script -# Entry point for the Nomarchy installer. - -INSTALLER="/etc/install.sh" -[[ ! -f "$INSTALLER" ]] && INSTALLER="/etc/nixos/nomarchy/installer/install.sh" - -if [[ -f "$INSTALLER" ]]; then - sudo "$INSTALLER" "$@" -else - echo "Error: Nomarchy installer not found at $INSTALLER" - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-install-docker-dbs b/features/scripts/utils/nomarchy-install-docker-dbs deleted file mode 100755 index 1e1c5dc..0000000 --- a/features/scripts/utils/nomarchy-install-docker-dbs +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Docker DBs Stub -# This script is a stub for a specialized tool. - -notify-send "Nomarchy" "The docker-dbs tool is not implemented in this version of Nomarchy. Please use standard podman/docker commands." -echo "Not implemented." -exit 1 diff --git a/features/scripts/utils/nomarchy-installed-summary b/features/scripts/utils/nomarchy-installed-summary deleted file mode 100755 index aad28e0..0000000 --- a/features/scripts/utils/nomarchy-installed-summary +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Print a curated "what the installer wrote" summary so the user can verify -# the system shape on first boot before they start customising. Reads from -# state.json where the schema persists the value, and falls back to live -# introspection for things the schema doesn't track (form factor, drives, -# active software profiles, FDE). -# -# Invoked as Step 0 of nomarchy-welcome and available as a standalone CLI. - -HOME_STATE="$HOME/.config/nomarchy/state.json" -SYS_STATE="/etc/nixos/state.json" - -jq_or_empty() { - local file="$1" - local query="$2" - [[ -f "$file" ]] || { echo ""; return; } - jq -r "$query // empty" "$file" 2>/dev/null || echo "" -} - -theme=$(jq_or_empty "$HOME_STATE" '.theme') -font=$(jq_or_empty "$HOME_STATE" '.font') -panel=$(jq_or_empty "$HOME_STATE" '.panelPosition') -tz=$(jq_or_empty "$SYS_STATE" '.timezone') -dns=$(jq_or_empty "$SYS_STATE" '.dns') -hybrid_gpu=$(jq_or_empty "$SYS_STATE" '.features.hybridGPU') - -# Form factor: battery presence (same signal the installer uses to auto-set -# nomarchy.{system.,}formFactor). `compgen` is a bash programmable-completion -# builtin, and the non-interactive bash this script gets wrapped with is -# compiled without it — so a glob into a nullglob array, not `compgen -G`. -shopt -s nullglob -batteries=( /sys/class/power_supply/BAT* ) -shopt -u nullglob -if (( ${#batteries[@]} )); then - form_factor="laptop" -else - form_factor="desktop" -fi - -# Active software profiles: heuristic detection. The installer bakes the -# user's pick into the generated home.nix as concrete `home.packages` / -# system toggles rather than persisting a profile list, so we check for -# the marker package of each profile. -profiles=() -command -v docker >/dev/null 2>&1 && profiles+=("Dev") -command -v steam >/dev/null 2>&1 && profiles+=("Gaming") -command -v libreoffice >/dev/null 2>&1 && profiles+=("Office") -command -v obs >/dev/null 2>&1 && profiles+=("Media") -command -v rg >/dev/null 2>&1 && profiles+=("CLI Utils") - -profiles_str="None" -if (( ${#profiles[@]} > 0 )); then - profiles_str="$(IFS=', '; echo "${profiles[*]}")" -fi - -# FDE: any crypt device present means the install used LUKS. -luks="No" -if lsblk -no TYPE 2>/dev/null | grep -q '^crypt$'; then - luks="Yes" -fi - -# Drives: target disks + their mounted root/boot/crypt partitions. Filter -# noise (loop/rom/zram) so the table reads like an install receipt. -drives=$(lsblk -no NAME,SIZE,TYPE,MOUNTPOINT 2>/dev/null \ - | grep -Ev '^(loop|sr|zram)' \ - | awk 'NF>=3 && $3 ~ /^(disk|part|crypt)$/' \ - || true) - -version_line=$(nomarchy-version 2>/dev/null || echo "Nomarchy") -hostname_line="$(hostname 2>/dev/null || echo "")" - -# Render via gum format (markdown). Fallback to plain text when gum isn't -# on PATH — keeps the script callable from minimal contexts like recovery. -render() { - cat <<MD -# $version_line on \`$hostname_line\` - -| Setting | Value | -| --- | --- | -| Theme | ${theme:-—} | -| Font | ${font:-—} | -| Panel | ${panel:-—} | -| Form factor | $form_factor | -| Timezone | ${tz:-—} | -| DNS | ${dns:-—} | -| Hybrid GPU | ${hybrid_gpu:-false} | -| Profiles | $profiles_str | -| FDE (LUKS) | $luks | - -## Drives - -\`\`\` -$drives -\`\`\` -MD -} - -if command -v gum >/dev/null 2>&1; then - render | gum format -else - render -fi diff --git a/features/scripts/utils/nomarchy-launch-about b/features/scripts/utils/nomarchy-launch-about deleted file mode 100755 index f2d98d5..0000000 --- a/features/scripts/utils/nomarchy-launch-about +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Launch the fastfetch TUI that gives information about the current system. - -exec nomarchy-launch-or-focus-tui "bash -c 'fastfetch; read -n 1 -s'" diff --git a/features/scripts/utils/nomarchy-launch-audio b/features/scripts/utils/nomarchy-launch-audio deleted file mode 100755 index 700b120..0000000 --- a/features/scripts/utils/nomarchy-launch-audio +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Launch the Nomarchy audio controls TUI (provided by wiremix). - -nomarchy-launch-or-focus-tui wiremix diff --git a/features/scripts/utils/nomarchy-launch-bluetooth b/features/scripts/utils/nomarchy-launch-bluetooth deleted file mode 100755 index a8ea185..0000000 --- a/features/scripts/utils/nomarchy-launch-bluetooth +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Launch the Nomarchy bluetooth controls TUI (provided by bluetui). -# Also attempts to unblock bluetooth service if rfkill had blocked it. - -rfkill unblock bluetooth -exec nomarchy-launch-or-focus-tui bluetui diff --git a/features/scripts/utils/nomarchy-launch-browser b/features/scripts/utils/nomarchy-launch-browser deleted file mode 100755 index 658c1d4..0000000 --- a/features/scripts/utils/nomarchy-launch-browser +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -e - -# Launch the default browser as determined by xdg-settings. -# Automatically converts --private into the correct flag for the given browser. - -default_browser=$(xdg-settings get default-web-browser) -browser_exec=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$default_browser 2>/dev/null | head -1) - -if $browser_exec --help | grep -q MOZ_LOG; then - private_flag="--private-window" -elif [[ $browser_exec =~ edge ]]; then - private_flag="--inprivate" -else - private_flag="--incognito" -fi - -exec setsid uwsm-app -- "$browser_exec" "${@/--private/$private_flag}" diff --git a/features/scripts/utils/nomarchy-launch-editor b/features/scripts/utils/nomarchy-launch-editor deleted file mode 100755 index 9f33743..0000000 --- a/features/scripts/utils/nomarchy-launch-editor +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -# Launch the default editor as determined by $EDITOR (set via ~/.config/uwsm/default) (or nvim if missing). -# Starts suitable editors in a terminal window and otherwise as a regular application. - -nomarchy-cmd-present "$EDITOR" || EDITOR=nvim - -case "$EDITOR" in -nvim | vim | nano | micro | hx | helix | fresh) - exec nomarchy-launch-tui "$EDITOR" "$@" - ;; -*) - exec setsid uwsm-app -- "$EDITOR" "$@" - ;; -esac diff --git a/features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation b/features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation deleted file mode 100755 index a2f6653..0000000 --- a/features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Launch a floating terminal with the Nomarchy logo presentation, then execute the command passed in, and finally end with the nomarchy-show-done presentation. -# Used by actions such as Update System. - -cmd="$*" -exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nomarchy.terminal --title=Nomarchy -e bash -c "nomarchy-show-logo; $cmd; if (( \$? != 130 )); then nomarchy-show-done; fi" diff --git a/features/scripts/utils/nomarchy-launch-or-focus b/features/scripts/utils/nomarchy-launch-or-focus deleted file mode 100755 index 0682a35..0000000 --- a/features/scripts/utils/nomarchy-launch-or-focus +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -set -e - -# Launch or focus on a given command identified by the passed in window-pattern. -# Use by some default bindings, like the one for Spotify, to ensure there is only one instance of the application open. - -if (($# == 0)); then - echo "Usage: nomarchy-launch-or-focus [window-pattern] [launch-command]" - exit 1 -fi - -WINDOW_PATTERN="$1" -LAUNCH_COMMAND="${2:-"uwsm-app -- $WINDOW_PATTERN"}" -WINDOW_ADDRESS=$(hyprctl clients -j | jq -r --arg p "$WINDOW_PATTERN" '.[]|select((.class|test("\\b" + $p + "\\b";"i")) or (.title|test("\\b" + $p + "\\b";"i")))|.address' | head -n1) - -if [[ -n $WINDOW_ADDRESS ]]; then - hyprctl dispatch focuswindow "address:$WINDOW_ADDRESS" -else - eval exec setsid $LAUNCH_COMMAND -fi diff --git a/features/scripts/utils/nomarchy-launch-or-focus-tui b/features/scripts/utils/nomarchy-launch-or-focus-tui deleted file mode 100755 index bcc6955..0000000 --- a/features/scripts/utils/nomarchy-launch-or-focus-tui +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -# Launch or focus on a given TUI identified by the passed in as the command. -# Use by commands like nomarchy-launch-wifi to ensure there is only one wifi configuration screen open. - -APP_ID="org.nomarchy.$(basename "$1")" -LAUNCH_COMMAND="nomarchy-launch-tui $@" - -exec nomarchy-launch-or-focus "$APP_ID" "$LAUNCH_COMMAND" diff --git a/features/scripts/utils/nomarchy-launch-or-focus-webapp b/features/scripts/utils/nomarchy-launch-or-focus-webapp deleted file mode 100755 index 0bc1b5b..0000000 --- a/features/scripts/utils/nomarchy-launch-or-focus-webapp +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -# Launch or focus on a given web app identified by the window-pattern. -# Use by some default bindings, like the one for WhatsApp, to ensure there is only one instance of the application open. - -if (($# == 0)); then - echo "Usage: nomarchy-launch-or-focus-webapp [window-pattern] [url-and-flags...]" - exit 1 -fi - -WINDOW_PATTERN="$1" -shift -LAUNCH_COMMAND="nomarchy-launch-webapp $@" - -exec nomarchy-launch-or-focus "$WINDOW_PATTERN" "$LAUNCH_COMMAND" diff --git a/features/scripts/utils/nomarchy-launch-screensaver b/features/scripts/utils/nomarchy-launch-screensaver deleted file mode 100755 index 324b7e6..0000000 --- a/features/scripts/utils/nomarchy-launch-screensaver +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -set -e - -# Launch the Nomarchy screensaver in the default terminal on the system with the correct font configuration. - -# Exit early if we don't have the tte show -if ! command -v tte &>/dev/null; then - exit 1 -fi - -# Exit early if screensave is already running -pgrep -f org.nomarchy.screensaver && exit 0 - -# Allow screensaver to be turned off but also force started -# Skip if screensaver is disabled in configuration -if [[ $NOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then - exit 0 -fi - - -# Silently quit Walker on overlay -walker -q - -focused=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name') -terminal=$(xdg-terminal-exec --print-id) - -for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do - hyprctl dispatch focusmonitor $m - - case $terminal in - *Alacritty*) - hyprctl dispatch exec -- \ - alacritty --class=org.nomarchy.screensaver \ - --config-file ~/.local/share/nomarchy/default/alacritty/screensaver.toml \ - -e nomarchy-cmd-screensaver - ;; - *ghostty*) - hyprctl dispatch exec -- \ - ghostty --class=org.nomarchy.screensaver \ - --config-file=~/.local/share/nomarchy/default/ghostty/screensaver \ - --font-size=18 \ - -e nomarchy-cmd-screensaver - ;; - *kitty*) - hyprctl dispatch exec -- \ - kitty --class=org.nomarchy.screensaver \ - --override font_size=18 \ - --override window_padding_width=0 \ - -e nomarchy-cmd-screensaver - ;; - *) - notify-send -u low "✋ Screensaver only runs in Alacritty, Ghostty, or Kitty" - ;; - esac -done - -hyprctl dispatch focusmonitor $focused diff --git a/features/scripts/utils/nomarchy-launch-tui b/features/scripts/utils/nomarchy-launch-tui deleted file mode 100755 index 547f2b0..0000000 --- a/features/scripts/utils/nomarchy-launch-tui +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Launch the TUI command passed in as an argument in the default terminal with an org.nomarchy.COMMAND app id for styling. - -exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nomarchy.$(basename $1) -e "$1" "${@:2}" diff --git a/features/scripts/utils/nomarchy-launch-walker b/features/scripts/utils/nomarchy-launch-walker deleted file mode 100644 index da9b4ef..0000000 --- a/features/scripts/utils/nomarchy-launch-walker +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -set -e - -# Wrapper to launch walker with elephant provider, or fallback to rofi if walker is missing. - -if command -v walker >/dev/null 2>&1; then - # The setsid backgrounded commands below MUST redirect all std fds to - # /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. - # 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 ! 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 - - # dmenu mode reads stdin and is invoked many times in quick succession - # by nomarchy-menu. Wrapping each call in uwsm-app (systemd-run --scope) - # creates a fresh transient scope per invocation, which breaks chained - # submenus — subsequent walker calls don't see a usable stdin and exit - # without showing anything. Invoke walker directly for dmenu. - if [[ "$*" == *"--dmenu"* ]]; then - exec walker "$@" - fi - - exec uwsm-app -- walker --width 644 --maxheight 300 --minheight 300 "$@" -elif command -v rofi >/dev/null 2>&1; then - # Convert walker arguments to rofi arguments if possible - # This is a very basic mapping for --dmenu - if [[ "$*" == *"--dmenu"* ]]; then - exec rofi -dmenu "$@" - else - exec rofi -show drun - fi -else - notify-send "Error" "Neither walker nor rofi found." -u critical - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-launch-webapp b/features/scripts/utils/nomarchy-launch-webapp deleted file mode 100755 index 28a60db..0000000 --- a/features/scripts/utils/nomarchy-launch-webapp +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -e - -# Launch the passed in URL as a web app in the default browser (or chromium if the default doesn't support --app). - -browser=$(xdg-settings get default-web-browser) - -case $browser in -google-chrome* | brave-browser* | microsoft-edge* | opera* | vivaldi* | helium*) ;; -*) browser="chromium.desktop" ;; -esac - -exec setsid uwsm-app -- $(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$browser 2>/dev/null | head -1) --app="$1" "${@:2}" diff --git a/features/scripts/utils/nomarchy-launch-wifi b/features/scripts/utils/nomarchy-launch-wifi deleted file mode 100755 index 32d7ab3..0000000 --- a/features/scripts/utils/nomarchy-launch-wifi +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Launch the Nomarchy wifi controls (using nmtui). -# Attempts to unblock the wifi service first in case it should be been blocked. - -rfkill unblock wifi -alacritty -e nmtui diff --git a/features/scripts/utils/nomarchy-lock-screen b/features/scripts/utils/nomarchy-lock-screen deleted file mode 100755 index 77aede2..0000000 --- a/features/scripts/utils/nomarchy-lock-screen +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -e - -# Locks the system using hyprlock, but not before ensuring 1password has also been locked, and the screensaver stopped. - -# Lock the screen -pidof hyprlock || hyprlock & - -# Set keyboard layout to default (first layout) -hyprctl switchxkblayout all 0 > /dev/null 2>&1 - -# Ensure 1password is locked -if pgrep -x "1password" >/dev/null; then - 1password --lock & -fi - -# Avoid running screensaver when locked -pkill -f org.nomarchy.screensaver diff --git a/features/scripts/utils/nomarchy-manual b/features/scripts/utils/nomarchy-manual deleted file mode 100755 index 90edb6b..0000000 --- a/features/scripts/utils/nomarchy-manual +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Open the Nomarchy docs index in the default handler. -# On an installed system the source tree lives at ~/.local/share/nomarchy/ -# (see SKILL.md's "Out of Scope" section), so the README — which links every -# doc in docs/ — is the canonical "open the manual" target. - -README="$HOME/.local/share/nomarchy/README.md" - -if [[ -f "$README" ]]; then - echo "Opening Nomarchy manual: $README" - xdg-open "$README" -else - notify-send "Nomarchy Manual" \ - "Source tree not found at $README. Try \`nomarchy-update\` to sync it." - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-menu b/features/scripts/utils/nomarchy-menu deleted file mode 100755 index 6fc1620..0000000 --- a/features/scripts/utils/nomarchy-menu +++ /dev/null @@ -1,371 +0,0 @@ -#!/bin/bash - -# Launch the Nomarchy Menu or takes a parameter to jump straight to a submenu. -# -# Deliberately runs WITHOUT `set -e`: this is an interactive UX loop, and -# action branches do `cmd; back_to <self>` so a failed action would abort -# the script under set -e and the menu would disappear without feedback. -# Soft-failure (the menu re-displays, the user picks again) is the right -# semantic here. - -export PATH="$HOME/.local/share/nomarchy/bin:$PATH" - -# Set to true when going directly to a submenu, so we can exit directly -BACK_TO_EXIT=false - -back_to() { - local parent_menu="$1" - - if [[ $BACK_TO_EXIT == "true" ]]; then - exit 0 - elif [[ -n $parent_menu ]]; then - "$parent_menu" - else - show_main_menu - fi -} - -toggle_existing_menu() { - if pgrep -f "walker.*--dmenu" >/dev/null; then - walker --close >/dev/null 2>&1 - exit 0 - fi -} - -menu() { - local prompt="$1" - local options="$2" - local extra="$3" - local preselect="$4" - - read -r -a args <<<"$extra" - - if [[ -n $preselect ]]; then - local index - index=$(echo -e "$options" | grep -nxF "$preselect" | cut -d: -f1) - if [[ -n $index ]]; then - args+=("-c" "$index") - fi - fi - - echo -e "$options" | nomarchy-launch-walker --dmenu --width 295 --minheight 1 --maxheight 630 -p "$prompt…" "${args[@]}" 2>/dev/null -} - -terminal() { - xdg-terminal-exec --app-id=org.nomarchy.terminal "$@" -} - -present_terminal() { - nomarchy-launch-floating-terminal-with-presentation $1 -} - -open_in_editor() { - notify-send -u low "Editing config file" "$1" - nomarchy-launch-editor "$1" -} - -show_learn_menu() { - case $(menu "Learn" " Keybindings\n Nomarchy\n Hyprland\n󰣇 Arch\n Neovim\n󱆃 Bash") in - *Keybindings*) nomarchy-menu-keybindings ;; - *Nomarchy*) nomarchy-manual ;; - *Hyprland*) nomarchy-launch-webapp "https://wiki.hypr.land/" ;; - *Arch*) nomarchy-launch-webapp "https://wiki.archlinux.org/title/Main_page" ;; - *Bash*) nomarchy-launch-webapp "https://devhints.io/bash" ;; - *Neovim*) nomarchy-launch-webapp "https://www.lazyvim.org/keymaps" ;; - *) back_to show_main_menu ;; - esac -} - -show_trigger_menu() { - case $(menu "Trigger" " Capture\n Share\n󰔎 Toggle\n Hardware") in - *Capture*) show_capture_menu ;; - *Share*) show_share_menu ;; - *Toggle*) show_toggle_menu ;; - *Hardware*) show_hardware_menu ;; - *) back_to show_main_menu ;; - esac -} - -show_capture_menu() { - case $(menu "Capture" " Screenshot\n Screenrecord\n󰃉 Color") in - *Screenshot*) nomarchy-cmd-screenshot ;; - *Screenrecord*) show_screenrecord_menu ;; - *Color*) pkill hyprpicker || hyprpicker -a ;; - *) back_to show_trigger_menu ;; - esac -} - -get_webcam_list() { - v4l2-ctl --list-devices 2>/dev/null | while IFS= read -r line; do - if [[ $line != $'\t'* && -n $line ]]; then - local name="$line" - IFS= read -r device || break - device=$(echo "$device" | tr -d '\t' | head -1) - [[ -n $device ]] && echo "$device $name" - fi - done -} - -show_webcam_select_menu() { - local devices=$(get_webcam_list) - local count=$(echo "$devices" | grep -c . 2>/dev/null || echo 0) - - if [[ -z $devices ]] || ((count == 0)); then - notify-send "No webcam devices found" -u critical -t 3000 - return 1 - fi - - if ((count == 1)); then - echo "$devices" | awk '{print $1}' - else - menu "Select Webcam" "$devices" | awk '{print $1}' - fi -} - -show_screenrecord_menu() { - nomarchy-cmd-screenrecord --stop-recording && exit 0 - - case $(menu "Screenrecord" " With no audio\n With desktop audio\n With desktop + microphone audio\n With desktop + microphone audio + webcam") in - *"With no audio") nomarchy-cmd-screenrecord ;; - *"With desktop audio") nomarchy-cmd-screenrecord --with-desktop-audio ;; - *"With desktop + microphone audio") nomarchy-cmd-screenrecord --with-desktop-audio --with-microphone-audio ;; - *"With desktop + microphone audio + webcam") - local device=$(show_webcam_select_menu) || { - back_to show_capture_menu - return - } - nomarchy-cmd-screenrecord --with-desktop-audio --with-microphone-audio --with-webcam --webcam-device="$device" - ;; - *) back_to show_capture_menu ;; - esac -} - -show_share_menu() { - case $(menu "Share" " Clipboard\n File \n Folder") in - *Clipboard*) nomarchy-cmd-share clipboard ;; - *File*) terminal bash -c "nomarchy-cmd-share file" ;; - *Folder*) terminal bash -c "nomarchy-cmd-share folder" ;; - *) back_to show_trigger_menu ;; - esac -} - -show_toggle_menu() { - case $(menu "Toggle" "󱄄 Screensaver\n󰔎 Nightlight\n󱫖 Idle Lock\n󰍜 Top Bar\n󱂬 Workspace Layout\n Window Gaps\n 1-Window Ratio\n󰍹 Display Scaling") in - - *Screensaver*) nomarchy-toggle-screensaver; back_to show_toggle_menu ;; - *Nightlight*) nomarchy-toggle-nightlight; back_to show_toggle_menu ;; - *Idle*) nomarchy-toggle-idle; back_to show_toggle_menu ;; - *Bar*) nomarchy-toggle-waybar; back_to show_toggle_menu ;; - *Layout*) nomarchy-hyprland-workspace-layout-toggle; back_to show_toggle_menu ;; - *Ratio*) nomarchy-hyprland-window-single-square-aspect-toggle; back_to show_toggle_menu ;; - *Gaps*) nomarchy-hyprland-window-gaps-toggle; back_to show_toggle_menu ;; - *Scaling*) nomarchy-hyprland-monitor-scaling-cycle; back_to show_toggle_menu ;; - *) back_to show_trigger_menu ;; - esac -} - -show_hardware_menu() { - case $(menu "Toggle" " Hybrid GPU") in - *"Hybrid GPU"*) present_terminal nomarchy-toggle-hybrid-gpu ;; - *) back_to show_trigger_menu ;; - esac -} - -show_style_menu() { - case $(menu "Style" "󰸌 Theme\n Font\n Background\n Hyprland\n󱄄 Screensaver\n About") in - *Theme*) show_theme_menu ;; - *Font*) show_font_menu ;; - *Background*) show_background_menu ;; - *Hyprland*) open_in_editor ~/.config/nomarchy/default/hypr/looknfeel.conf ;; - *Screensaver*) open_in_editor ~/.config/nomarchy/branding/screensaver.txt ;; - *About*) open_in_editor ~/.config/nomarchy/branding/about.txt ;; - *) back_to show_main_menu ;; - esac -} - -show_theme_menu() { - nomarchy-launch-walker -m menus:nomarchythemes --width 800 --minheight 400 - back_to show_style_menu -} - -show_background_menu() { - nomarchy-launch-walker -m menus:nomarchyBackgroundSelector --width 800 --minheight 400 - back_to show_style_menu -} - -show_font_menu() { - theme=$(menu "Font" "$(nomarchy-font-list)" "--width 350" "$(nomarchy-font-current)") - if [[ $theme == "CNCLD" || -z $theme ]]; then - back_to show_style_menu - else - nomarchy-font-set "$theme" - back_to show_font_menu - fi -} - -show_setup_menu() { - local options=" Audio\n Wifi\n󰂯 Bluetooth" - # Power Profile drives powerprofilesctl, which only exists when - # power-profiles-daemon is running. Laptops force it off in favour of - # TLP and desktops don't enable it, so gate the entry rather than - # offer an action that dies with "command not found". - command -v powerprofilesctl >/dev/null 2>&1 && options="$options\n󱐋 Power Profile" - options="$options\n System Sleep\n󰍹 Monitors" - [[ -f ~/.config/hypr/bindings.conf ]] && options="$options\n Keybindings" - [[ -f ~/.config/hypr/input.conf ]] && options="$options\n Input" - options="$options\n󰱔 DNS\n Security" - - case $(menu "Setup" "$options") in - *Audio*) nomarchy-launch-audio ;; - *Wifi*) nomarchy-launch-wifi ;; - *Bluetooth*) nomarchy-launch-bluetooth ;; - *Power*) show_setup_power_menu ;; - *System*) show_setup_system_menu ;; - *Monitors*) open_in_editor ~/.config/hypr/monitors.conf ;; - *Keybindings*) open_in_editor ~/.config/hypr/bindings.conf ;; - *Input*) open_in_editor ~/.config/hypr/input.conf ;; - *DNS*) present_terminal nomarchy-setup-dns ;; - *Security*) show_setup_security_menu ;; - *) back_to show_main_menu ;; - esac -} - -show_setup_power_menu() { - profile=$(menu "Power Profile" "$(nomarchy-powerprofiles-list)" "" "$(powerprofilesctl get)") - - if [[ $profile == "CNCLD" || -z $profile ]]; then - back_to show_setup_menu - else - powerprofilesctl set "$profile" - back_to show_setup_power_menu - fi -} - -show_setup_security_menu() { - case $(menu "Setup" "󰈷 Fingerprint\n Fido2") in - *Fingerprint*) present_terminal nomarchy-setup-fingerprint ;; - *Fido2*) present_terminal nomarchy-setup-fido2 ;; - *) back_to show_setup_menu ;; - esac -} - -show_setup_system_menu() { - local options="" - - if nomarchy-hibernation-available; then - options="$options\n󰤁 Disable Hibernate" - else - options="$options\n󰤁 Enable Hibernate" - fi - - case $(menu "System" "$options") in - *"Enable Hibernate"*) present_terminal nomarchy-hibernation-setup ;; - *"Disable Hibernate"*) present_terminal nomarchy-hibernation-remove ;; - *) back_to show_setup_menu ;; - esac -} - - - -show_update_menu() { - case $(menu "Update" "  Nomarchy\n󰸌 Extra Themes\n Process\n󰇅 Hardware\n Firmware\n Password\n Timezone\n Time") in - *Nomarchy*) present_terminal nomarchy-update ;; - *Themes*) present_terminal nomarchy-theme-update ;; - *Process*) show_update_process_menu ;; - *Hardware*) show_update_hardware_menu ;; - *Firmware*) present_terminal nomarchy-update-firmware ;; - *Timezone*) present_terminal nomarchy-tz-select ;; - *Time*) present_terminal nomarchy-update-time ;; - *Password*) show_update_password_menu ;; - *) back_to show_main_menu ;; - esac -} -show_update_process_menu() { - case $(menu "Restart" " Hypridle\n Hyprsunset\n Swayosd\n󰌧 Walker\n󰍜 Waybar") in - *Hypridle*) nomarchy-restart-hypridle; back_to show_update_process_menu ;; - *Hyprsunset*) nomarchy-restart-hyprsunset; back_to show_update_process_menu ;; - *Swayosd*) nomarchy-restart-swayosd; back_to show_update_process_menu ;; - *Walker*) nomarchy-restart-walker; back_to show_update_process_menu ;; - *Waybar*) nomarchy-restart-waybar; back_to show_update_process_menu ;; - *) back_to show_update_menu ;; - esac -} - - - -show_update_hardware_menu() { - case $(menu "Restart" " Audio\n󱚾 Wi-Fi\n󰂯 Bluetooth") in - *Audio*) present_terminal nomarchy-restart-pipewire ;; - *Wi-Fi*) present_terminal nomarchy-restart-wifi ;; - *Bluetooth*) present_terminal nomarchy-restart-bluetooth ;; - *) back_to show_update_menu ;; - esac -} - -show_update_password_menu() { - case $(menu "Update Password" " Drive Encryption\n User") in - *Drive*) present_terminal nomarchy-drive-set-password ;; - *User*) present_terminal passwd ;; - *) back_to show_update_menu ;; - esac -} - -show_about() { - nomarchy-launch-about -} - -show_system_menu() { - local options="󱄄 Screensaver\n Lock" - [[ $NOMARCHY_TOGGLE_SUSPEND != "false" ]] && options="$options\n󰒲 Suspend" - nomarchy-hibernation-available && options="$options\n󰤁 Hibernate" - options="$options\n󰍃 Logout\n󰜉 Restart\n󰐥 Shutdown" - - case $(menu "System" "$options") in - *Screensaver*) nomarchy-launch-screensaver force ;; - *Lock*) nomarchy-lock-screen ;; - *Suspend*) nomarchy-toggle-suspend ;; - *Hibernate*) systemctl hibernate ;; - *Logout*) nomarchy-system-logout ;; - *Restart*) nomarchy-system-reboot ;; - *Shutdown*) nomarchy-system-shutdown ;; - *) back_to show_main_menu ;; - esac -} - -show_main_menu() { - go_to_menu "$(menu "Go" "󰀻 Apps\n󰧑 Learn\n󱓞 Trigger\n Style\n Setup\n Update\n About\n System")" -} - -go_to_menu() { - case "${1,,}" in - *apps*) nomarchy-launch-walker ;; - *learn*) show_learn_menu ;; - *symbols*) nomarchy-launch-walker -m symbols ;; - *trigger*) show_trigger_menu ;; - *toggle*) show_toggle_menu ;; - *share*) show_share_menu ;; - *background*) show_background_menu ;; - *capture*) show_capture_menu ;; - *style*) show_style_menu ;; - *theme*) show_theme_menu ;; - *screenrecord*) show_screenrecord_menu ;; - *setup*) show_setup_menu ;; - *power*) show_setup_power_menu ;; - *update*) show_update_menu ;; - *about*) show_about ;; - *system*) show_system_menu ;; - esac -} - -# Allow user extensions and overrides -USER_EXTENSIONS="$HOME/.config/nomarchy/extensions/menu.sh" -[[ -f $USER_EXTENSIONS ]] && source "$USER_EXTENSIONS" - -toggle_existing_menu - -if [[ -n $1 ]]; then - BACK_TO_EXIT=true - go_to_menu "$1" -else - show_main_menu -fi diff --git a/features/scripts/utils/nomarchy-menu-keybindings b/features/scripts/utils/nomarchy-menu-keybindings deleted file mode 100755 index 360237e..0000000 --- a/features/scripts/utils/nomarchy-menu-keybindings +++ /dev/null @@ -1,248 +0,0 @@ -#!/bin/bash -set -e - -# Display Hyprland keybindings defined in your configuration using walker for an interactive search menu. - -declare -A KEYCODE_SYM_MAP - -build_keymap_cache() { - local keymap - keymap="$(xkbcli compile-keymap)" || { - echo "Failed to compile keymap" >&2 - return 1 - } - - while IFS=, read -r code sym; do - [[ -z $code || -z $sym ]] && continue - KEYCODE_SYM_MAP["$code"]="$sym" - done < <( - awk ' - BEGIN { sec = "" } - /xkb_keycodes/ { sec = "codes"; next } - /xkb_symbols/ { sec = "syms"; next } - sec == "codes" { - if (match($0, /<([A-Za-z0-9_]+)>\s*=\s*([0-9]+)\s*;/, m)) code_by_name[m[1]] = m[2] - } - sec == "syms" { - if (match($0, /key\s*<([A-Za-z0-9_]+)>\s*\{\s*\[\s*([^, \]]+)/, m)) sym_by_name[m[1]] = m[2] - } - END { - for (k in code_by_name) { - c = code_by_name[k] - s = sym_by_name[k] - if (c != "" && s != "" && s != "NoSymbol") print c "," s - } - } - ' <<<"$keymap" - ) -} - -lookup_keycode_cached() { - printf '%s\n' "${KEYCODE_SYM_MAP[$1]}" -} - -parse_keycodes() { - local start end elapsed - [[ ${DEBUG:-0} == "1" ]] && start=$(date +%s.%N) - while IFS= read -r line; do - if [[ $line =~ code:([0-9]+) ]]; then - code="${BASH_REMATCH[1]}" - symbol=$(lookup_keycode_cached "$code" "$XKB_KEYMAP_CACHE") - echo "${line/code:${code}/$symbol}" - elif [[ $line =~ mouse:([0-9]+) ]]; then - code="${BASH_REMATCH[1]}" - - case "$code" in - 272) symbol="LEFT MOUSE BUTTON" ;; - 273) symbol="RIGHT MOUSE BUTTON" ;; - 274) symbol="MIDDLE MOUSE BUTTON" ;; - *) symbol="mouse:${code}" ;; - esac - - echo "${line/mouse:${code}/$symbol}" - else - echo "$line" - fi - done - - if [[ $DEBUG == "1" ]]; then - end=$(date +%s.%N) - # fall back to awk if bc is missing - if command -v bc >/dev/null 2>&1; then - elapsed=$(echo "$end - $start" | bc) - else - elapsed=$(awk -v s="$start" -v e="$end" 'BEGIN{printf "%.6f", (e - s)}') - fi - echo "[DEBUG] parse_keycodes elapsed: ${elapsed}s" >&2 - fi -} - -# Fetch dynamic keybindings from Hyprland -# -# Also do some pre-processing: -# - Remove standard Nomarchy bin path prefix -# - Remove uwsm prefix -# - Map numeric modifier key mask to a textual rendition -# - Output comma-separated values that the parser can understand -dynamic_bindings() { - hyprctl -j binds | - jq -r '.[] | {modmask, key, keycode, description, dispatcher, arg} | "\(.modmask),\(.key)@\(.keycode),\(.description),\(.dispatcher),\(.arg)"' | - sed -r \ - -e 's/null//' \ - -e 's,~/.local/share/nomarchy/bin/,,' \ - -e 's,uwsm app -- ,,' \ - -e 's,uwsm-app -- ,,' \ - -e 's/@0//' \ - -e 's/,@/,code:/' \ - -e 's/^0,/,/' \ - -e 's/^1,/SHIFT,/' \ - -e 's/^4,/CTRL,/' \ - -e 's/^5,/SHIFT CTRL,/' \ - -e 's/^8,/ALT,/' \ - -e 's/^9,/SHIFT ALT,/' \ - -e 's/^12,/CTRL ALT,/' \ - -e 's/^13,/SHIFT CTRL ALT,/' \ - -e 's/^64,/SUPER,/' \ - -e 's/^65,/SUPER SHIFT,/' \ - -e 's/^68,/SUPER CTRL,/' \ - -e 's/^69,/SUPER SHIFT CTRL,/' \ - -e 's/^72,/SUPER ALT,/' \ - -e 's/^73,/SUPER SHIFT ALT,/' \ - -e 's/^76,/SUPER CTRL ALT,/' \ - -e 's/^77,/SUPER SHIFT CTRL ALT,/' -} - -# Hardcoded bindings, like the copy-url extension and such -static_bindings() { - echo "SHIFT ALT,L,Copy URL from Web App,extension,copy-url" -} - -# Parse and format keybindings -# -# `awk` does the heavy lifting: -# - Set the field separator to a comma ','. -# - Joins the key combination (e.g., "SUPER + Q"). -# - Joins the command that the key executes. -# - Prints everything in a nicely aligned format. -parse_bindings() { - awk -F, ' -{ - # Combine the modifier and key (first two fields) - key_combo = $1 " + " $2; - - # Clean up: strip leading "+" if present, trim spaces - gsub(/^[ \t]*\+?[ \t]*/, "", key_combo); - gsub(/[ \t]+$/, "", key_combo); - - # Use description, if set - action = $3; - - if (action == "") { - # Reconstruct the command from the remaining fields - for (i = 4; i <= NF; i++) { - action = action $i (i < NF ? "," : ""); - } - - # Clean up trailing commas, remove leading "exec, ", and trim - sub(/,$/, "", action); - gsub(/(^|,)[[:space:]]*exec[[:space:]]*,?/, "", action); - gsub(/^[ \t]+|[ \t]+$/, "", action); - gsub(/[ \t]+/, " ", key_combo); # Collapse multiple spaces to one - - # Escape XML entities - gsub(/&/, "\\&", action); - gsub(/</, "\\<", action); - gsub(/>/, "\\>", action); - gsub(/"/, "\\"", action); - gsub(/'"'"'/, "\\'", action); - } - - if (action != "") { - printf "%-35s → %s\n", key_combo, action; - } -}' -} - -prioritize_entries() { - awk ' - { - line = $0 - prio = 50 - if (match(line, /Terminal/)) prio = 0 - if (match(line, /Tmux/)) prio = 1 - if (match(line, /Browser/) && !match(line, /Browser[[:space:]]*\(/) && !match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 2 - if (match(line, /File manager/) && !match(line, /File manager \(cwd\)/)) prio = 3 - if (match(line, /Launch apps/)) prio = 4 - if (match(line, /Nomarchy menu/)) prio = 5 - if (match(line, /System menu/)) prio = 6 - if (match(line, /Theme menu/)) prio = 7 - if (match(line, /Full screen/)) prio = 8 - if (match(line, /Full width/)) prio = 9 - if (match(line, /Close window/)) prio = 10 - if (match(line, /Close all windows/)) prio = 11 - if (match(line, /Lock system/)) prio = 12 - if (match(line, /Toggle window floating/)) prio = 13 - if (match(line, /Toggle window split/)) prio = 14 - if (match(line, /Pop window/)) prio = 15 - if (match(line, /Universal/)) prio = 16 - if (match(line, /Clipboard/)) prio = 17 - if (match(line, /Audio controls/)) prio = 18 - if (match(line, /Bluetooth controls/)) prio = 19 - if (match(line, /Wifi controls/)) prio = 20 - if (match(line, /Emoji picker/)) prio = 21 - if (match(line, /Color picker/)) prio = 22 - if (match(line, /Screenshot/)) prio = 23 - if (match(line, /Screenrecording/)) prio = 24 - if (match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 25 - if (match(line, /File manager \(cwd\)/)) prio = 26 - if (match(line, /(Switch|Next|Former|Previous).*workspace/)) prio = 27 - if (match(line, /Move window to workspace/)) prio = 28 - if (match(line, /Move window silently to workspace/)) prio = 29 - if (match(line, /Swap window/)) prio = 30 - if (match(line, /Move window focus/)) prio = 31 - if (match(line, /Move window$/)) prio = 32 - if (match(line, /Resize window/)) prio = 33 - if (match(line, /Expand window/)) prio = 34 - if (match(line, /Shrink window/)) prio = 35 - if (match(line, /scratchpad/)) prio = 36 - if (match(line, /notification/)) prio = 37 - if (match(line, /Toggle window transparency/)) prio = 38 - if (match(line, /Toggle workspace gaps/)) prio = 39 - if (match(line, /Toggle nightlight/)) prio = 40 - if (match(line, /Toggle locking/)) prio = 41 - if (match(line, /group/)) prio = 94 - if (match(line, /Scroll active workspace/)) prio = 95 - if (match(line, /Cycle to/)) prio = 96 - if (match(line, /Reveal active/)) prio = 97 - if (match(line, /Apple Display/)) prio = 98 - if (match(line, /XF86/)) prio = 99 - - # print "priority<TAB>line" - printf "%d\t%s\n", prio, line - }' | - sort -k1,1n -k2,2 | - cut -f2- -} - -output_keybindings() { - build_keymap_cache - - { - dynamic_bindings - static_bindings - } | - sort -u | - parse_keycodes | - parse_bindings | - prioritize_entries -} - -if [[ $1 == "--print" || $1 == "-p" ]]; then - output_keybindings -else - monitor_height=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .height') - menu_height=$((monitor_height * 40 / 100)) - - output_keybindings | - walker --dmenu -p 'Keybindings' --width 800 --height "$menu_height" -fi diff --git a/features/scripts/utils/nomarchy-notification-dismiss b/features/scripts/utils/nomarchy-notification-dismiss deleted file mode 100755 index edba138..0000000 --- a/features/scripts/utils/nomarchy-notification-dismiss +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -set -e - -# Dismiss a mako notification on the basis of its summary. Used by the first-run notifications to dismiss them after clicking for action. - -if (($# == 0)); then - echo "Usage: nomarchy-notification-dismiss <summary>" - exit 1 -fi - -# Find the first notification whose 'summary' matches the regex in $1 -output=$(makoctl list) - -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 diff --git a/features/scripts/utils/nomarchy-on-boot b/features/scripts/utils/nomarchy-on-boot deleted file mode 100755 index 5c2bcdf..0000000 --- a/features/scripts/utils/nomarchy-on-boot +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy on-boot initialization script. -# Automatically detects the hardware, applies necessary runtime tweaks, -# and sets the correct screen resolution/scaling. - -# 1. Automatically configure optimal screen resolution and scaling -nomarchy-hyprland-monitor-scaling-cycle >/dev/null 2>&1 - -# 2. Hardware-specific runtime tweaks -if nomarchy-hw-match "Laptop 16"; then - # Framework 16 specific tweaks - nomarchy-theme-set-keyboard-f16 >/dev/null 2>&1 -fi - -if nomarchy-hw-asus-rog; then - # Asus ROG specific tweaks - nomarchy-theme-set-keyboard-asus-rog >/dev/null 2>&1 -fi - -# 3. Hardware detection -# Superseded by the installer's hardware-db.sh and declarative selection. -exit 0 diff --git a/features/scripts/utils/nomarchy-pkg-add b/features/scripts/utils/nomarchy-pkg-add deleted file mode 100755 index b58ab91..0000000 --- a/features/scripts/utils/nomarchy-pkg-add +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -e - -PKG_NAME="$1" - -if [ -z "$PKG_NAME" ]; then - echo "Usage: nomarchy-pkg-add <package-name>" - exit 1 -fi - -STATE_FILE="$HOME/.config/home-manager/user-packages.json" -mkdir -p "$(dirname "$STATE_FILE")" - -if [ ! -f "$STATE_FILE" ]; then - echo "[]" > "$STATE_FILE" -fi - -if jq -e --arg pkg "$PKG_NAME" '. | index($pkg)' "$STATE_FILE" >/dev/null; then - echo "Package $PKG_NAME is already in your user-packages.json" - exit 0 -fi - -# Append package to the JSON array safely -jq --arg pkg "$PKG_NAME" '. + [$pkg]' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" - -echo "Package $PKG_NAME added declaratively to $STATE_FILE." -echo "Applying changes with nomarchy-env-update..." -nomarchy-env-update diff --git a/features/scripts/utils/nomarchy-pkg-remove b/features/scripts/utils/nomarchy-pkg-remove deleted file mode 100755 index 7ccfad6..0000000 --- a/features/scripts/utils/nomarchy-pkg-remove +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -e - -PKG_NAME="$1" - -if [ -z "$PKG_NAME" ]; then - echo "Usage: nomarchy-pkg-remove <package-name>" - exit 1 -fi - -STATE_FILE="$HOME/.config/home-manager/user-packages.json" - -if [ ! -f "$STATE_FILE" ]; then - echo "No packages managed by nomarchy-pkg yet." - exit 0 -fi - -if ! jq -e --arg pkg "$PKG_NAME" '. | index($pkg)' "$STATE_FILE" >/dev/null; then - echo "Package $PKG_NAME is not in your user-packages.json" - exit 0 -fi - -# Remove package from the JSON array safely -jq --arg pkg "$PKG_NAME" '. - [$pkg]' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" - -echo "Package $PKG_NAME removed declaratively from $STATE_FILE." -echo "Applying changes with nomarchy-env-update..." -nomarchy-env-update diff --git a/features/scripts/utils/nomarchy-preflight-migration b/features/scripts/utils/nomarchy-preflight-migration deleted file mode 100644 index 887a8c3..0000000 --- a/features/scripts/utils/nomarchy-preflight-migration +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash -set -e -# Nomarchy Pre-flight State Migration -# Migrates legacy state files into the unified state.json before Nix evaluation - -STATE_DIR="$HOME/.config/nomarchy" -OLD_STATE_DIR="$HOME/.config/home-manager" -OLD_TOGGLES_DIR="$HOME/.local/state/nomarchy/toggles" -IDLE_STATE_FILE="$OLD_STATE_DIR/idle-state.json" -NIGHTLIGHT_STATE_FILE="$OLD_STATE_DIR/hyprsunset-state.json" -HYPRLAND_STATE_FILE="$OLD_STATE_DIR/hyprland-state.json" -THEME_STATE_FILE="$OLD_STATE_DIR/theme-state.nix" -WALLPAPER_STATE_FILE="$OLD_STATE_DIR/wallpaper-state.nix" -FONT_STATE_FILE="$OLD_STATE_DIR/font-state.nix" -OLD_STATE_FILE="$OLD_STATE_DIR/state.json" -NEW_STATE_FILE="$STATE_DIR/state.json" - -# We expect jq to be in PATH (it's a dependency of nomarchy-scripts) -JQ="jq" - -mkdir -p "$STATE_DIR" -[[ ! -f $NEW_STATE_FILE ]] && echo "{}" > "$NEW_STATE_FILE" - -# 0. Migrate from old home-manager state.json location -if [[ -f "$OLD_STATE_FILE" ]] && [[ "$OLD_STATE_FILE" != "$NEW_STATE_FILE" ]]; then - # Merge old state into new state - TMP_FILE=$(mktemp) - $JQ -s '.[0] * .[1]' "$OLD_STATE_FILE" "$NEW_STATE_FILE" > "$TMP_FILE" && mv "$TMP_FILE" "$NEW_STATE_FILE" - rm "$OLD_STATE_FILE" 2>/dev/null || true -fi - -# 1. Migrate .local/state/nomarchy/toggles -if [[ -d $OLD_TOGGLES_DIR ]]; then - for file in "$OLD_TOGGLES_DIR"/*; do - [[ -e "$file" ]] || continue - filename=$(basename "$file") - case "$filename" in - suspend-off) - $JQ '.suspend = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - screensaver-off) - $JQ '.screensaver = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - skip-vscode-theme-changes) - $JQ '.skipVsCodeTheme = true' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - esac - rm "$file" - done - rmdir "$OLD_TOGGLES_DIR" 2>/dev/null || true -fi - -# 2. Migrate existing JSON state files -if [[ -f $IDLE_STATE_FILE ]]; then - ENABLED=$($JQ '.enabled' "$IDLE_STATE_FILE") - if [[ "$ENABLED" == "true" || "$ENABLED" == "false" ]]; then - $JQ --argjson val "$ENABLED" '.idle = $val' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - fi - rm "$IDLE_STATE_FILE" -fi - -if [[ -f $NIGHTLIGHT_STATE_FILE ]]; then - ENABLED=$($JQ '.enabled' "$NIGHTLIGHT_STATE_FILE") - TEMP=$($JQ '.temperature' "$NIGHTLIGHT_STATE_FILE") - $JQ --argjson enabled "$ENABLED" --argjson temp "$TEMP" '.nightlight = $enabled | .nightlightTemperature = $temp' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$NIGHTLIGHT_STATE_FILE" -fi - -if [[ -f $HYPRLAND_STATE_FILE ]]; then - GAPS_OUT=$($JQ '.gaps_out' "$HYPRLAND_STATE_FILE") - GAPS_IN=$($JQ '.gaps_in' "$HYPRLAND_STATE_FILE") - BORDER_SIZE=$($JQ '.border_size' "$HYPRLAND_STATE_FILE") - $JQ --argjson go "$GAPS_OUT" --argjson gi "$GAPS_IN" --argjson bs "$BORDER_SIZE" '.hyprland = {"gaps_out": $go, "gaps_in": $gi, "border_size": $bs}' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$HYPRLAND_STATE_FILE" -fi - -# 3. Migrate plaintext string state files -if [[ -f $THEME_STATE_FILE ]]; then - THEME=$(cat "$THEME_STATE_FILE" | tr -d '\n') - $JQ --arg theme "$THEME" '.theme = $theme' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$THEME_STATE_FILE" -fi - -if [[ -f $WALLPAPER_STATE_FILE ]]; then - WALLPAPER=$(cat "$WALLPAPER_STATE_FILE" | tr -d '\n') - $JQ --arg wp "$WALLPAPER" '.wallpaper = $wp' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$WALLPAPER_STATE_FILE" -fi - -if [[ -f $FONT_STATE_FILE ]]; then - FONT=$(cat "$FONT_STATE_FILE" | tr -d '\n') - $JQ --arg font "$FONT" '.font = $font' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$FONT_STATE_FILE" -fi diff --git a/features/scripts/utils/nomarchy-refresh-config b/features/scripts/utils/nomarchy-refresh-config deleted file mode 100755 index 45b9b71..0000000 --- a/features/scripts/utils/nomarchy-refresh-config +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -set -e - -# nomarchy-refresh-config: Restore a config file in ~/.config to its stock -# version. The pristine copies live in ~/.local/share/nomarchy/config (deployed -# by core/home/configs.nix), so this works on any system without needing the -# flake source tree on disk. -# -# Usage: nomarchy-refresh-config <relative-path-under-~/.config> -# Example: nomarchy-refresh-config fastfetch/config.jsonc - -CONFIG_FILE=$1 - -if [[ -z $CONFIG_FILE ]]; then - echo "Usage: nomarchy-refresh-config <config-path>" - exit 1 -fi - -STOCK_BASE="${XDG_DATA_HOME:-$HOME/.local/share}/nomarchy/config" -SOURCE_FILE="$STOCK_BASE/$CONFIG_FILE" -DEST_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/$CONFIG_FILE" - -if [[ ! -f "$SOURCE_FILE" ]]; then - echo "Error: Stock configuration for $CONFIG_FILE not found in $STOCK_BASE." - notify-send -u critical "Error" "No stock config for $CONFIG_FILE." 2>/dev/null || true - exit 1 -fi - -echo "Refreshing $DEST_FILE from stock $SOURCE_FILE..." -mkdir -p "$(dirname "$DEST_FILE")" -cp "$SOURCE_FILE" "$DEST_FILE" -notify-send "Config Refreshed" "$CONFIG_FILE has been restored to defaults." 2>/dev/null || true diff --git a/features/scripts/utils/nomarchy-refresh-fastfetch b/features/scripts/utils/nomarchy-refresh-fastfetch deleted file mode 100755 index 4d70d0c..0000000 --- a/features/scripts/utils/nomarchy-refresh-fastfetch +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Overwrite the user config for fastfetch with the Nomarchy default. - -nomarchy-refresh-config fastfetch/config.jsonc diff --git a/features/scripts/utils/nomarchy-refresh-hyprland b/features/scripts/utils/nomarchy-refresh-hyprland deleted file mode 100755 index 5a99626..0000000 --- a/features/scripts/utils/nomarchy-refresh-hyprland +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy Refresh Hyprland Script -# Reloads the Hyprland configuration. - -set -e - -if command -v hyprctl &>/dev/null; then - hyprctl reload - notify-send -u low " Hyprland configuration reloaded" -else - echo "Error: hyprctl not found." - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-refresh-waybar b/features/scripts/utils/nomarchy-refresh-waybar deleted file mode 100755 index 74ecaf1..0000000 --- a/features/scripts/utils/nomarchy-refresh-waybar +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy Refresh Waybar Script -# Restarts Waybar to apply new configuration or theme. - -set -e - -if command -v nomarchy-restart-waybar &>/dev/null; then - nomarchy-restart-waybar - notify-send -u low "󰍜 Waybar refreshed" -else - echo "Error: nomarchy-restart-waybar not found." - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-reinstall b/features/scripts/utils/nomarchy-reinstall deleted file mode 100755 index c5f3518..0000000 --- a/features/scripts/utils/nomarchy-reinstall +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy Reinstall Script -# Performs a fresh 'switch' to the current declarative state. - -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 "Performing system reinstall from $REPO_DIR..." -# --refresh forces a download of flake inputs if they've changed upstream but not in lock -sudo nixos-rebuild switch --flake "$REPO_DIR#default" --refresh --impure - -echo "Reinstall complete." diff --git a/features/scripts/utils/nomarchy-restart-app b/features/scripts/utils/nomarchy-restart-app deleted file mode 100755 index 755ec83..0000000 --- a/features/scripts/utils/nomarchy-restart-app +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -set -e - -# Restart an application by killing it and relaunching via uwsm. -# Usage: nomarchy-restart-app <application-name> [application-args...] -# -# We wait for the old process to actually exit before launching the -# new one. Without the wait, the new instance starts while the old -# one's wayland surface is still mapped — visible as ghosting on -# layer-shell apps and double-instance briefly for everything else. -# Same race that produced the waybar theme-switch artifacts before -# 386da51 moved waybar to a SIGUSR2-reload path. - -app="$1" -shift || true - -if pgrep -x "$app" >/dev/null 2>&1; then - pkill -x "$app" 2>/dev/null || true - # Poll for graceful exit, up to ~1.5s. - for _ in $(seq 1 15); do - pgrep -x "$app" >/dev/null 2>&1 || break - sleep 0.1 - done - # Anything still running: SIGKILL it. Without this, a misbehaving - # process can hold the surface indefinitely and the new instance - # races on top. - pkill -KILL -x "$app" 2>/dev/null || true -fi - -setsid uwsm-app -- "$app" "$@" >/dev/null 2>&1 & diff --git a/features/scripts/utils/nomarchy-restart-btop b/features/scripts/utils/nomarchy-restart-btop deleted file mode 100755 index 018fa98..0000000 --- a/features/scripts/utils/nomarchy-restart-btop +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Reload btop configuration (used by the Nomarchy theme switching). - -pkill -SIGUSR2 btop diff --git a/features/scripts/utils/nomarchy-restart-hypridle b/features/scripts/utils/nomarchy-restart-hypridle deleted file mode 100755 index d171971..0000000 --- a/features/scripts/utils/nomarchy-restart-hypridle +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Restart the hypridle service (used for idle detection and auto-lock). - -nomarchy-restart-app hypridle diff --git a/features/scripts/utils/nomarchy-restart-hyprsunset b/features/scripts/utils/nomarchy-restart-hyprsunset deleted file mode 100755 index 21a503d..0000000 --- a/features/scripts/utils/nomarchy-restart-hyprsunset +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Restart the hyprsunset service (used for blue light filtering/night light). - -nomarchy-restart-app hyprsunset diff --git a/features/scripts/utils/nomarchy-restart-opencode b/features/scripts/utils/nomarchy-restart-opencode deleted file mode 100755 index d2b1e7a..0000000 --- a/features/scripts/utils/nomarchy-restart-opencode +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Reload opencode configuration (used by the Nomarchy theme switching). - -if pgrep -x opencode >/dev/null; then - killall -SIGUSR2 opencode -fi diff --git a/features/scripts/utils/nomarchy-restart-swayosd b/features/scripts/utils/nomarchy-restart-swayosd deleted file mode 100755 index 1587001..0000000 --- a/features/scripts/utils/nomarchy-restart-swayosd +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e - -nomarchy-restart-app swayosd-server diff --git a/features/scripts/utils/nomarchy-restart-terminal b/features/scripts/utils/nomarchy-restart-terminal deleted file mode 100755 index c8aee00..0000000 --- a/features/scripts/utils/nomarchy-restart-terminal +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -e - -if [[ -f ~/.config/alacritty/alacritty.toml ]]; then - touch ~/.config/alacritty/alacritty.toml -fi - -if pgrep -x kitty >/dev/null; then - killall -SIGUSR1 kitty >/dev/null -fi - -if pgrep -x ghostty >/dev/null; then - killall -SIGUSR2 ghostty -fi diff --git a/features/scripts/utils/nomarchy-restart-walker b/features/scripts/utils/nomarchy-restart-walker deleted file mode 100755 index d990cdc..0000000 --- a/features/scripts/utils/nomarchy-restart-walker +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -set -e - -# Walker (with runAsService = true) and elephant are Home Manager user -# services. Restart via systemd so the new process inherits the full graphical -# session environment — the pkill/uwsm-app dance left a window where the -# launcher would come back up with stale theming. - -restart_services() { - if systemctl --user list-unit-files elephant.service >/dev/null 2>&1; then - systemctl --user restart elephant.service - fi - - if systemctl --user list-unit-files walker.service >/dev/null 2>&1; then - systemctl --user restart walker.service - elif systemctl --user is-enabled app-walker@autostart.service &>/dev/null; then - systemctl --user restart app-walker@autostart.service - else - echo -e "\e[31mUnable to restart Walker -- RESTART MANUALLY\e[0m" - fi -} - -if (( EUID == 0 )); then - SCRIPT_OWNER=$(stat -c '%U' "$0") - USER_UID=$(id -u "$SCRIPT_OWNER") - systemd-run --uid="$SCRIPT_OWNER" --setenv=XDG_RUNTIME_DIR="/run/user/$USER_UID" \ - bash -c "$(declare -f restart_services); restart_services" -else - restart_services -fi diff --git a/features/scripts/utils/nomarchy-restart-waybar b/features/scripts/utils/nomarchy-restart-waybar deleted file mode 100755 index 8f749f9..0000000 --- a/features/scripts/utils/nomarchy-restart-waybar +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -e - -# Reload waybar in place when it's already running. SIGUSR2 makes waybar -# re-read its config.jsonc and CSS (including @import-ed files like -# ~/.config/nomarchy/current/theme/waybar.css that theme-switch rewrites) -# without destroying its layer-shell surface. A full systemctl restart -# tears the surface down and creates a new one back-to-back, leaving a -# frame or two where Hyprland hasn't cleared the dead surface yet — that's -# the "old on top of new with artifacts" you see after a theme switch on -# the live ISO. -if pgrep -x waybar >/dev/null; then - exec pkill -SIGUSR2 -x waybar -fi - -# Not running yet — start it. Prefer the HM systemd user unit -# (programs.waybar.systemd.enable = true). `systemctl cat` returns -# non-zero when the unit doesn't exist; `list-unit-files` does not. -if systemctl --user cat waybar.service >/dev/null 2>&1; then - exec systemctl --user start waybar.service -fi - -# Fallback for systems where waybar isn't managed by systemd. -nomarchy-restart-app waybar diff --git a/features/scripts/utils/nomarchy-skill b/features/scripts/utils/nomarchy-skill deleted file mode 100755 index 19c6768..0000000 --- a/features/scripts/utils/nomarchy-skill +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Skill Script -# Displays the Nomarchy Skill documentation for agents and power users. - -SKILL_FILE="$HOME/.config/nomarchy-skill/SKILL.md" - -if [[ ! -f "$SKILL_FILE" ]]; then - # Fallback to repo location if managed by nix - SKILL_FILE="/etc/nixos/nomarchy/core/home/config/nomarchy-skill/SKILL.md" -fi - -if [[ ! -f "$SKILL_FILE" ]]; then - # Final fallback to standard config dir - SKILL_FILE="$HOME/.config/nomarchy-skill/SKILL.md" -fi - -if [[ -f "$SKILL_FILE" ]]; then - glow "$SKILL_FILE" -else - echo "Error: Nomarchy Skill documentation not found." - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-state b/features/scripts/utils/nomarchy-state deleted file mode 100755 index 84a685a..0000000 --- a/features/scripts/utils/nomarchy-state +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -e - -# Manage persistent runtime state files for Nomarchy indicators. -# Usage: nomarchy-state <set|clear> <state-name-or-pattern> -# Used to track whether things like reboot, restart, etc are required. -# DO NOT use this for configuration toggles (suspend, screensaver, etc). -# Use declarative Nomarchy NixOS home-manager options for those instead. - -STATE_DIR="$HOME/.local/state/nomarchy" -mkdir -p "$STATE_DIR" - -COMMAND="$1" -STATE_NAME="$2" - -if [[ -z $COMMAND ]]; then - echo "Usage: nomarchy-state <set|clear> <state-name-or-pattern>" - exit 1 -fi - -if [[ -z $STATE_NAME ]]; then - echo "Usage: nomarchy-state $COMMAND <state-name>" - exit 1 -fi - -case "$COMMAND" in -set) touch "$STATE_DIR/$STATE_NAME" ;; -clear) find "$STATE_DIR" -maxdepth 1 -type f -name "$STATE_NAME" -delete ;; -esac diff --git a/features/scripts/utils/nomarchy-state-write b/features/scripts/utils/nomarchy-state-write deleted file mode 100755 index 95e8e3d..0000000 --- a/features/scripts/utils/nomarchy-state-write +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash -set -e -# Nomarchy Atomic State Write Helper -# Provides atomic JSON state updates with flock to prevent race conditions -# -# Usage: -# nomarchy-state-write <key> <value> [--type string|bool|number|json] -# nomarchy-state-write --file <path> <key> <value> [--type ...] -# -# Examples: -# nomarchy-state-write theme "nord" -# nomarchy-state-write idle true --type bool -# nomarchy-state-write hyprland '{"gaps_in": 5}' --type json -# nomarchy-state-write --file /etc/nixos/state.json dns "Cloudflare" - -set -e - -# Default state file -STATE_FILE="$HOME/.config/nomarchy/state.json" -VALUE_TYPE="string" - -# Parse arguments -while [[ $# -gt 0 ]]; do - case $1 in - --file) - STATE_FILE="$2" - shift 2 - ;; - --type) - VALUE_TYPE="$2" - shift 2 - ;; - *) - if [[ -z "$KEY" ]]; then - KEY="$1" - elif [[ -z "$VALUE" ]]; then - VALUE="$1" - fi - shift - ;; - esac -done - -if [[ -z "$KEY" ]] || [[ -z "$VALUE" ]]; then - echo "Usage: nomarchy-state-write <key> <value> [--type string|bool|number|json]" - echo " nomarchy-state-write --file <path> <key> <value> [--type ...]" - exit 1 -fi - -# Ensure directory exists -mkdir -p "$(dirname "$STATE_FILE")" - -# Initialize file if it doesn't exist -[[ ! -f "$STATE_FILE" ]] && echo "{}" > "$STATE_FILE" - -# Create lock file path -LOCK_FILE="${STATE_FILE}.lock" - -# Perform atomic update with flock -( - flock -x 200 - - TMP_FILE=$(mktemp) - trap "rm -f '$TMP_FILE'" EXIT - - case "$VALUE_TYPE" in - string) - jq --arg val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" - ;; - bool) - if [[ "$VALUE" == "true" ]] || [[ "$VALUE" == "false" ]]; then - jq --argjson val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" - else - echo "Error: Boolean value must be 'true' or 'false'" - exit 1 - fi - ;; - number) - jq --argjson val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" - ;; - json) - jq --argjson val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" - ;; - *) - echo "Error: Unknown type '$VALUE_TYPE'. Use string, bool, number, or json." - exit 1 - ;; - esac - - # 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" diff --git a/features/scripts/utils/nomarchy-sync b/features/scripts/utils/nomarchy-sync deleted file mode 100755 index 5b0dca8..0000000 --- a/features/scripts/utils/nomarchy-sync +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env bash - -# nomarchy-sync: Automate backing up and restoring Nomarchy declarative configurations and dynamic state. - -set -e - -if [[ -z $1 ]]; then - echo "Usage: nomarchy-sync <push|pull> [repo-url]" - echo " push: Backup current state to the configured repository." - echo " pull: Restore state from the configured repository and apply updates." - exit 1 -fi - -COMMAND=$1 -REPO_URL=$2 - -STATE_DIR="$HOME/.config/home-manager" -CONFIG_DIR="/etc/nixos" - -# Identify the target repo directory (we use a local dot-folder to stage things) -SYNC_DIR="$HOME/.local/share/nomarchy-sync" - -mkdir -p "$SYNC_DIR" -cd "$SYNC_DIR" - -if [ ! -d ".git" ]; then - if [[ -z $REPO_URL ]]; then - echo "Error: No Git repository configured. Please provide a repo-url for the first run:" - echo "Example: nomarchy-sync push git@github.com:username/nomarchy-backup.git" - exit 1 - fi - git init - git remote add origin "$REPO_URL" - # Basic configuration for automated commits - git config user.name "Nomarchy Sync" - git config user.email "sync@nomarchy.local" -fi - -if [[ "$COMMAND" == "push" ]]; then - echo "Synchronizing local configuration and state to backup repository..." - - # 1. Copy user flakes and nix files - mkdir -p "$SYNC_DIR/nixos" - cp -r "$CONFIG_DIR/flake.nix" "$CONFIG_DIR/system.nix" "$CONFIG_DIR/home.nix" "$SYNC_DIR/nixos/" 2>/dev/null || true - - # 2. Copy state files - mkdir -p "$SYNC_DIR/state" - cp -r "$STATE_DIR"/*.nix "$STATE_DIR"/*.json "$SYNC_DIR/state/" 2>/dev/null || true - - # 3. Commit and Push - git add . - if git diff-index --quiet HEAD --; then - echo "No changes to backup." - else - git commit -m "Auto-sync backup from $(date +'%Y-%m-%d %H:%M:%S')" - # We use a try-catch pattern for push because upstream might not exist yet - echo "Pushing to remote..." - git push -u origin master || git push -u origin main - echo "Backup successful." - fi - -elif [[ "$COMMAND" == "pull" ]]; then - echo "Pulling backup from remote repository..." - git fetch - # Hard reset to exactly match remote - git reset --hard origin/master 2>/dev/null || git reset --hard origin/main - - echo "Restoring configuration and state..." - - # 1. Restore user flakes (Requires sudo) - if [ -d "$SYNC_DIR/nixos" ]; then - sudo cp -r "$SYNC_DIR/nixos/"* "$CONFIG_DIR/" - sudo chmod -R u+w "$CONFIG_DIR" - fi - - # 2. Restore state files - if [ -d "$SYNC_DIR/state" ]; then - mkdir -p "$STATE_DIR" - cp -r "$SYNC_DIR/state/"* "$STATE_DIR/" - fi - - echo "Applying updates..." - # Apply the restored settings - sudo nixos-rebuild switch --flake "$CONFIG_DIR#default" - home-manager switch --flake "$CONFIG_DIR#default" --impure - - echo "Restore successful." - -else - echo "Unknown command: $COMMAND" - exit 1 -fi \ No newline at end of file diff --git a/features/scripts/utils/nomarchy-sync-nix-state b/features/scripts/utils/nomarchy-sync-nix-state deleted file mode 100755 index 01a7d23..0000000 --- a/features/scripts/utils/nomarchy-sync-nix-state +++ /dev/null @@ -1,68 +0,0 @@ -#!/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" diff --git a/features/scripts/utils/nomarchy-sys-update b/features/scripts/utils/nomarchy-sys-update deleted file mode 100644 index cb11f6e..0000000 --- a/features/scripts/utils/nomarchy-sys-update +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy System Update Script -# 1. Applies system-wide NixOS changes - -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 - -# The installer generates `nixosConfigurations.<hostname>` (see -# installer/install.sh: `nixosConfigurations.$HOSTNAME`), so the flake target -# must match the current host. The previous `#default` literal worked only -# for a development host that happened to be named "default" and silently -# broke every toggle script (nomarchy-tz-select, nomarchy-wifi-powersave, -# nomarchy-setup-{dns,fido2,fingerprint}, nomarchy-toggle-hybrid-gpu) on a -# real install. -HOSTNAME_ATTR=$(hostname) -if [ -z "$HOSTNAME_ATTR" ]; then - echo "Error: could not determine hostname for flake attribute." >&2 - exit 1 -fi - -echo "Applying system-level changes from $REPO_DIR#$HOSTNAME_ATTR..." -sudo nixos-rebuild switch --flake "$REPO_DIR#$HOSTNAME_ATTR" - -echo "System update complete." diff --git a/features/scripts/utils/nomarchy-test-installer b/features/scripts/utils/nomarchy-test-installer deleted file mode 100755 index 134faae..0000000 --- a/features/scripts/utils/nomarchy-test-installer +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Build and run the Nomarchy Installer VM for testing. - -echo "Launching Nomarchy Installer VM..." -nix run .#installerVm diff --git a/features/scripts/utils/nomarchy-test-vm b/features/scripts/utils/nomarchy-test-vm deleted file mode 100755 index a0faf76..0000000 --- a/features/scripts/utils/nomarchy-test-vm +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Build and run the Nomarchy VM (installed environment) for testing. - -echo "Building Nomarchy VM (Installed Environment)..." -echo "Note: To test the INSTALLER, run 'nomarchy-test-installer' instead." -nix build .#nixosConfigurations.default.config.system.build.vm - -if [ $? -eq 0 ]; then - # Drop any persisted VM disk so activation runs against fresh state. - # Without this, broken symlinks from a prior activation survive rebuilds - # and mask fixes on the next launch. - rm -f ./nomarchy.qcow2 - echo "Success! Launching VM..." - ./result/bin/run-nomarchy-vm -else - echo "Error: VM build failed." - exit 1 -fi diff --git a/features/scripts/utils/nomarchy-theme b/features/scripts/utils/nomarchy-theme deleted file mode 100755 index b04bc5d..0000000 --- a/features/scripts/utils/nomarchy-theme +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Theme Helper -# Usage: nomarchy-theme [selector|set <name>|list] - -COMMAND="$1" - -case "$COMMAND" in - set) - shift - nomarchy-theme-set "$@" - ;; - list) - nomarchy-theme-list - ;; - selector|*) - nomarchy-launch-walker -m menus:nomarchythemes --width 800 --minheight 400 - ;; -esac diff --git a/features/scripts/utils/nomarchy-toggle-notification-silencing b/features/scripts/utils/nomarchy-toggle-notification-silencing deleted file mode 100755 index 294687e..0000000 --- a/features/scripts/utils/nomarchy-toggle-notification-silencing +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -makoctl mode -t do-not-disturb - -if makoctl mode | grep -q 'do-not-disturb'; then - notify-send -u low "󰂛 Silenced notifications" -else - notify-send -u low "󰂚 Enabled notifications" -fi - -pkill -RTMIN+10 waybar diff --git a/features/scripts/utils/nomarchy-toggle-screensaver b/features/scripts/utils/nomarchy-toggle-screensaver deleted file mode 100755 index 21e658e..0000000 --- a/features/scripts/utils/nomarchy-toggle-screensaver +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Toggles the screensaver availability. -# Hybrid: updates state.json and runs env-update for persistence. - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" -mkdir -p "$STATE_DIR" - -# Initialize if doesn't exist -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -if [[ $NOMARCHY_TOGGLE_SCREENSAVER == "false" ]]; then - NEW_VALUE="true" - notify-send -u low " Screensaver enabled" -else - NEW_VALUE="false" - notify-send -u low " Screensaver disabled" -fi - -nomarchy-state-write screensaver "$NEW_VALUE" --type bool - -echo "Screensaver state set to $NEW_VALUE. Updating environment..." - -nomarchy-env-update diff --git a/features/scripts/utils/nomarchy-toggle-waybar b/features/scripts/utils/nomarchy-toggle-waybar deleted file mode 100755 index 804d82a..0000000 --- a/features/scripts/utils/nomarchy-toggle-waybar +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Toggles the waybar top bar. Writes the new state to state.json (so the -# next home-manager rebuild realigns programs.waybar.enable via -# features/desktop/waybar/default.nix) and flips the running systemd -# user unit for instant feedback. - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" -mkdir -p "$STATE_DIR" - -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -if [[ $NOMARCHY_TOGGLE_WAYBAR == "false" ]]; then - NEW_VALUE="true" - systemctl --user start waybar.service 2>/dev/null || true - notify-send -u low " Top bar enabled" -else - NEW_VALUE="false" - systemctl --user stop waybar.service 2>/dev/null || true - notify-send -u low " Top bar disabled" -fi - -nomarchy-state-write waybar "$NEW_VALUE" --type bool - -echo "Waybar state set to $NEW_VALUE." diff --git a/features/scripts/utils/nomarchy-tui-install b/features/scripts/utils/nomarchy-tui-install deleted file mode 100755 index 2d6397a..0000000 --- a/features/scripts/utils/nomarchy-tui-install +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash - -set -e - -if (( $# != 4 )); then - echo -e "\e[32mLet's create a TUI shortcut you can start with the app launcher.\n\e[0m" - APP_NAME=$(gum input --prompt "Name> " --placeholder "My TUI") - APP_EXEC=$(gum input --prompt "Launch Command> " --placeholder "lazydocker or bash -c 'dust; read -n 1 -s'") - WINDOW_STYLE=$(gum choose --header "Window style" float tile) - ICON_URL=$(gum input --prompt "Icon URL> " --placeholder "See https://dashboardicons.com (must use PNG or SVG!)") -else - APP_NAME="$1" - APP_EXEC="$2" - WINDOW_STYLE="$3" - ICON_URL="$4" -fi - -if [[ -z $APP_NAME || -z $APP_EXEC || -z $ICON_URL ]]; then - echo "You must set app name, app command, and icon URL!" - exit 1 -fi - -ICON_DIR="$HOME/.local/share/applications/icons" -DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop" - -if [[ ! $ICON_URL =~ ^https?:// ]] && [[ -f $ICON_URL ]]; then - ICON_PATH="$ICON_URL" -else - ICON_PATH="$ICON_DIR/$APP_NAME.png" - mkdir -p "$ICON_DIR" - if ! curl -sL -o "$ICON_PATH" "$ICON_URL"; then - echo "Error: Failed to download icon." - exit 1 - fi -fi - -if [[ $WINDOW_STYLE == "float" ]]; then - APP_CLASS="TUI.float" -else - APP_CLASS="TUI.tile" -fi - -cat >"$DESKTOP_FILE" <<EOF -[Desktop Entry] -Version=1.0 -Name=$APP_NAME -Comment=$APP_NAME -Exec=xdg-terminal-exec --app-id=$APP_CLASS -e $APP_EXEC -Terminal=false -Type=Application -Icon=$ICON_PATH -StartupNotify=true -EOF - -chmod +x "$DESKTOP_FILE" - -if (( $# != 4 )); then - echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n" -fi diff --git a/features/scripts/utils/nomarchy-tui-remove b/features/scripts/utils/nomarchy-tui-remove deleted file mode 100755 index 3b97df8..0000000 --- a/features/scripts/utils/nomarchy-tui-remove +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -set -e - -ICON_DIR="$HOME/.local/share/applications/icons" -DESKTOP_DIR="$HOME/.local/share/applications/" - -if (( $# == 0 )); then - # Find all TUIs - while IFS= read -r -d '' file; do - if grep -qE '^Exec=.*(\$TERMINAL|xdg-terminal-exec).*-e' "$file"; then - TUIS+=("$(basename "${file%.desktop}")") - fi - done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0) - - if ((${#TUIS[@]})); then - IFS=$'\n' SORTED_TUIS=($(sort <<<"${TUIS[*]}")) - unset IFS - APP_NAMES_STRING=$(gum choose --no-limit --header "Select TUI to remove..." --selected-prefix="✗ " "${SORTED_TUIS[@]}") - # Convert newline-separated string to array - APP_NAMES=() - while IFS= read -r line; do - [[ -n $line ]] && APP_NAMES+=("$line") - done <<< "$APP_NAMES_STRING" - else - echo "No TUIs to remove." - exit 1 - fi -else - # Use array to preserve spaces in app names - APP_NAMES=("$@") -fi - -if (( ${#APP_NAMES[@]} == 0 )); then - echo "You must provide TUI names." - exit 1 -fi - -for APP_NAME in "${APP_NAMES[@]}"; do - rm -f "$DESKTOP_DIR/$APP_NAME.desktop" - rm -f "$ICON_DIR/$APP_NAME.png" - echo "Removed $APP_NAME" -done diff --git a/features/scripts/utils/nomarchy-tui-remove-all b/features/scripts/utils/nomarchy-tui-remove-all deleted file mode 100755 index 0ae4dde..0000000 --- a/features/scripts/utils/nomarchy-tui-remove-all +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Remove all TUIs installed via nomarchy-tui-install. -# Identifies TUIs by their Exec pattern (xdg-terminal-exec --app-id=TUI.). - -set -e - -APP_DIR="${1:-$HOME/.local/share/applications}" -ICON_DIR="$HOME/.local/share/applications/icons" - -echo "Scanning for TUIs in $APP_DIR..." - -tui_desktop_files=() -while IFS= read -r -d '' file; do - if grep -q "Exec=xdg-terminal-exec --app-id=TUI\." "$file" 2>/dev/null; then - tui_desktop_files+=("$file") - fi -done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null) - -if (( ${#tui_desktop_files[@]} == 0 )); then - echo "No TUIs found." - exit 0 -fi - -for file in "${tui_desktop_files[@]}"; do - app_name=$(basename "$file" .desktop) - echo "Removing TUI: $app_name" - rm -f "$file" - rm -f "$ICON_DIR/$app_name.png" -done - -if command -v update-desktop-database &>/dev/null; then - update-desktop-database "$APP_DIR" &>/dev/null || true -fi - -echo "TUIs removed successfully." diff --git a/features/scripts/utils/nomarchy-update b/features/scripts/utils/nomarchy-update deleted file mode 100755 index c74552f..0000000 --- a/features/scripts/utils/nomarchy-update +++ /dev/null @@ -1,79 +0,0 @@ -#!/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 diff --git a/features/scripts/utils/nomarchy-update-available b/features/scripts/utils/nomarchy-update-available deleted file mode 100644 index 3124a78..0000000 --- a/features/scripts/utils/nomarchy-update-available +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Update Available Script -# Checks if flake updates are available and returns info for Waybar - -REPO_DIR="" -if [ -f "/etc/nixos/flake.nix" ]; then - REPO_DIR="/etc/nixos" -elif [ -f "/etc/nomarchy/flake.nix" ]; then - REPO_DIR="/etc/nomarchy" -fi - -if [ -z "$REPO_DIR" ]; then - echo "Nomarchy repo not found." - exit 0 -fi - -# 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 - -# Get the current locked revision for Nomarchy -LOCKED_REV=$(jq -r '.nodes.nomarchy.locked.rev // empty' "$REPO_DIR/flake.lock" 2>/dev/null) - -if [ -z "$LOCKED_REV" ]; then - # Could be named something else or missing, fallback to generic icon - echo "" - exit 0 -fi - -# 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 - -# 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 diff --git a/features/scripts/utils/nomarchy-update-firmware b/features/scripts/utils/nomarchy-update-firmware deleted file mode 100755 index 6deb164..0000000 --- a/features/scripts/utils/nomarchy-update-firmware +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy Firmware Update Script -# Wraps fwupdmgr to update hardware firmware. - -set -e - -echo "Checking for firmware updates..." -if ! fwupdmgr get-updates; then - echo "No updates available or fwupd service not running." - exit 0 -fi - -if gum confirm "Apply firmware updates? (Requires a reboot to finalize some updates)"; then - fwupdmgr update - echo "Firmware update complete. Please reboot if prompted." -else - echo "Update cancelled." -fi diff --git a/features/scripts/utils/nomarchy-update-time b/features/scripts/utils/nomarchy-update-time deleted file mode 100755 index 08c92fd..0000000 --- a/features/scripts/utils/nomarchy-update-time +++ /dev/null @@ -1,18 +0,0 @@ -#!/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." diff --git a/features/scripts/utils/nomarchy-upload-log b/features/scripts/utils/nomarchy-upload-log deleted file mode 100755 index 33a9795..0000000 --- a/features/scripts/utils/nomarchy-upload-log +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -# Nomarchy Log Upload Script -# Uploads stdin or a file to a pastebin service. - -set -e - -if [[ -t 0 ]] && [[ -z $1 ]]; then - echo "Usage: some-command | nomarchy-upload-log" - echo " nomarchy-upload-log <file>" - exit 1 -fi - -# Use ix.io as the default pastebin -if [[ -n $1 ]]; then - # --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 -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" -if command -v wl-copy &>/dev/null; then - echo "$url" | wl-copy - echo "Link copied to clipboard." -fi diff --git a/features/scripts/utils/nomarchy-version b/features/scripts/utils/nomarchy-version deleted file mode 100755 index 4d6776f..0000000 --- a/features/scripts/utils/nomarchy-version +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Version Script -# Prints the current Nomarchy version based on the upstream NixOS channel. - -VERSION="25.11.0" -CODENAME="Sovereign" - -echo "Nomarchy v${VERSION} (${CODENAME})" diff --git a/features/scripts/utils/nomarchy-wallpaper b/features/scripts/utils/nomarchy-wallpaper deleted file mode 100755 index 1c342f5..0000000 --- a/features/scripts/utils/nomarchy-wallpaper +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Wallpaper Helper -# Usage: nomarchy-wallpaper [selector|set <path>|next] - -COMMAND="$1" - -case "$COMMAND" in - set) - shift - # Wallpaper set usually involves writing to state.json and calling swww - nomarchy-theme-bg-set "$@" - ;; - next) - nomarchy-theme-bg-next - ;; - selector|*) - nomarchy-launch-walker -m menus:nomarchyBackgroundSelector --width 800 --minheight 400 - ;; -esac diff --git a/features/scripts/utils/nomarchy-webapp-install b/features/scripts/utils/nomarchy-webapp-install deleted file mode 100755 index 1d3e7b8..0000000 --- a/features/scripts/utils/nomarchy-webapp-install +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash - -set -e - -ICON_DIR="$HOME/.local/share/applications/icons" - -if (( $# < 3 )); then - echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m" - APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app") - APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com") - if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then - APP_URL="https://$APP_URL" - fi - - # Try to fetch favicon automatically first. - FAVICON_URL="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128" - mkdir -p "$ICON_DIR" - if curl -fsSL -o "$ICON_DIR/$APP_NAME.png" "$FAVICON_URL" && [[ -s $ICON_DIR/$APP_NAME.png ]]; then - ICON_REF="$APP_NAME.png" - else - ICON_REF=$(gum input --prompt "Icon URL> " --placeholder "Could not fetch favicon automatically. Enter PNG icon URL (see https://dashboardicons.com)") - fi - - CUSTOM_EXEC="" - MIME_TYPES="" - INTERACTIVE_MODE=true -else - APP_NAME="$1" - APP_URL="$2" - if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then - APP_URL="https://$APP_URL" - fi - ICON_REF="$3" - CUSTOM_EXEC="$4" # Optional custom exec command - MIME_TYPES="$5" # Optional mime types - INTERACTIVE_MODE=false -fi - -# Ensure valid execution -if [[ -z $APP_NAME || -z $APP_URL ]]; then - echo "You must set app name and app URL!" - exit 1 -fi - -# Resolve icon from URL or from a local icon name. -mkdir -p "$ICON_DIR" - -if [[ -z $ICON_REF ]]; then - ICON_REF="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128" -fi - -if [[ $ICON_REF =~ ^https?:// ]]; then - ICON_PATH="$ICON_DIR/$APP_NAME.png" - if ! curl -fsSL -o "$ICON_PATH" "$ICON_REF" || [[ ! -s $ICON_PATH ]]; then - echo "Error: Failed to download icon." - exit 1 - fi -else - ICON_PATH="$ICON_DIR/$ICON_REF" -fi - -# Use custom exec if provided, otherwise default behavior -EXEC_COMMAND="${CUSTOM_EXEC:-nomarchy-launch-webapp $APP_URL}" - -# Create application .desktop file -DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop" - -cat >"$DESKTOP_FILE" <<EOF -[Desktop Entry] -Version=1.0 -Name=$APP_NAME -Comment=$APP_NAME -Exec=$EXEC_COMMAND -Terminal=false -Type=Application -Icon=$ICON_PATH -StartupNotify=true -EOF - -# Add mime types if provided -if [[ -n $MIME_TYPES ]]; then - echo "MimeType=$MIME_TYPES" >>"$DESKTOP_FILE" -fi - -chmod +x "$DESKTOP_FILE" - -if [[ $INTERACTIVE_MODE == "true" ]]; then - echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n" -fi diff --git a/features/scripts/utils/nomarchy-webapp-remove b/features/scripts/utils/nomarchy-webapp-remove deleted file mode 100755 index b927abc..0000000 --- a/features/scripts/utils/nomarchy-webapp-remove +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -set -e - -ICON_DIR="$HOME/.local/share/applications/icons" -DESKTOP_DIR="$HOME/.local/share/applications/" - -if (( $# == 0 )); then - # Find all web apps - while IFS= read -r -d '' file; do - if grep -q '^Exec=.*\(nomarchy-launch-webapp\|nomarchy-webapp-handler\).*' "$file"; then - WEB_APPS+=("$(basename "${file%.desktop}")") - fi - done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0) - - if ((${#WEB_APPS[@]})); then - IFS=$'\n' SORTED_WEB_APPS=($(sort <<<"${WEB_APPS[*]}")) - unset IFS - APP_NAMES_STRING=$(gum choose --no-limit --header "Select web app to remove..." --selected-prefix="✗ " "${SORTED_WEB_APPS[@]}") - # Convert newline-separated string to array - APP_NAMES=() - while IFS= read -r line; do - [[ -n $line ]] && APP_NAMES+=("$line") - done <<< "$APP_NAMES_STRING" - else - echo "No web apps to remove." - exit 1 - fi -else - # Use array to preserve spaces in app names - APP_NAMES=("$@") -fi - -if (( ${#APP_NAMES[@]} == 0 )); then - echo "You must select at least one web app to remove." - exit 1 -fi - -for APP_NAME in "${APP_NAMES[@]}"; do - rm -f "$DESKTOP_DIR/$APP_NAME.desktop" - rm -f "$ICON_DIR/$APP_NAME.png" - echo "Removed $APP_NAME" -done diff --git a/features/scripts/utils/nomarchy-webapp-remove-all b/features/scripts/utils/nomarchy-webapp-remove-all deleted file mode 100755 index 80b5c50..0000000 --- a/features/scripts/utils/nomarchy-webapp-remove-all +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Remove all web apps installed via nomarchy-webapp-install. -# Identifies web apps by their Exec pattern (nomarchy-launch-webapp or nomarchy-webapp-handler). - -set -e - -APP_DIR="${1:-$HOME/.local/share/applications}" -ICON_DIR="$HOME/.local/share/applications/icons" - -echo "Scanning for web apps in $APP_DIR..." - -webapp_desktop_files=() -while IFS= read -r -d '' file; do - if grep -q "Exec=nomarchy-launch-webapp\|Exec=nomarchy-webapp-handler" "$file" 2>/dev/null; then - webapp_desktop_files+=("$file") - fi -done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null) - -if (( ${#webapp_desktop_files[@]} == 0 )); then - echo "No web apps found." - exit 0 -fi - -for file in "${webapp_desktop_files[@]}"; do - app_name=$(basename "$file" .desktop) - echo "Removing web app: $app_name" - rm -f "$file" - rm -f "$ICON_DIR/$app_name.png" -done - -if command -v update-desktop-database &>/dev/null; then - update-desktop-database "$APP_DIR" &>/dev/null || true -fi - -echo "Web apps removed successfully." diff --git a/features/scripts/utils/nomarchy-welcome b/features/scripts/utils/nomarchy-welcome deleted file mode 100755 index 6e87382..0000000 --- a/features/scripts/utils/nomarchy-welcome +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash -set -e - -STATE_FILE="$HOME/.config/nomarchy/state.json" - -# Check if welcome wizard has already been completed -if [ -f "$STATE_FILE" ]; then - DONE=$(jq -r '.welcome_done' "$STATE_FILE" 2>/dev/null) - if [ "$DONE" == "true" ]; then - exit 0 - fi -fi - -# Ensure we have a terminal for the wizard -if [ -z "$TERMINAL_WIZARD" ]; then - export TERMINAL_WIZARD=1 - alacritty -e "$0" - exit 0 -fi - -gum style \ - --foreground 212 --border-foreground 212 --border double \ - --align center --width 50 --margin "1 2" --padding "2 4" \ - "Nomarchy" "The Professional NixOS Desktop" - -echo "Welcome! Let's personalize your new system." -echo "" - -# 0. Show what the installer actually wrote so the user can verify the -# install shape (theme, font, profiles, drives, FDE, form factor) before -# diving into customisation. Available standalone via the same command. -nomarchy-installed-summary -echo "" -gum input --placeholder "Press Enter to continue with customisation…" >/dev/null || true -echo "" - -# 1. Select initial theme -echo "Step 1: Choose your starting theme" -SELECTED_THEME="$(nomarchy-theme-list | gum filter --placeholder 'Select a theme...')" -if [[ -n "$SELECTED_THEME" ]]; then - THEME_ID=$(echo "$SELECTED_THEME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') - nomarchy-theme-set "$THEME_ID" --no-update -fi - -# 2. Select initial font -echo "Step 2: Choose your preferred font" -SELECTED_FONT="$(nomarchy-font-list | gum filter --placeholder 'Select a font...')" -if [[ -n "$SELECTED_FONT" ]]; then - nomarchy-font-set "$SELECTED_FONT" --no-update -fi - -# 3. Select panel position -echo "Step 3: Choose your preferred panel position" -POSITION=$(gum choose "top" "bottom") -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 "" - echo "Live ISO detected. Skipping home.nix generation and git repo check." - nomarchy-env-update - nomarchy-state-write welcome_done true --type bool - gum style --foreground 82 "Setup complete! Enjoy your Nomarchy experience." - sleep 3 - exit 0 -fi - -# 4. Setup Local Repo (Crucial for nomarchy-env-update to work) -echo "" -echo "Step 4: Git Repository Check" -echo "Nomarchy relies on a local git repository for declarative updates." -if [ ! -d "/etc/nixos/.git" ]; then - echo "Warning: /etc/nixos is not a git repository. Declarative updates might fail." - if gum confirm "Would you like to initialize /etc/nixos as a git repo?"; then - sudo git -C /etc/nixos init - sudo git -C /etc/nixos add . - sudo git -C /etc/nixos commit -m "Initial Nomarchy System Commit" - fi -fi - -# 5. Success -echo "" -echo "Applying all changes..." -nomarchy-env-update -nomarchy-state-write welcome_done true --type bool - -# Remove legacy flag file if it exists -rm -f "$HOME/.config/nomarchy/.first-run-done" - -gum style --foreground 82 "Setup complete! Enjoy your Nomarchy experience." -nomarchy-hook post-install -sleep 3 diff --git a/features/scripts/utils/nomarchy-windows-vm b/features/scripts/utils/nomarchy-windows-vm deleted file mode 100755 index e6cd6b2..0000000 --- a/features/scripts/utils/nomarchy-windows-vm +++ /dev/null @@ -1,443 +0,0 @@ -#!/bin/bash -set -e -COMPOSE_FILE="$HOME/.config/windows/docker-compose.yml" - -check_prerequisites() { - local DISK_SIZE_GB=${1:-64} - local REQUIRED_SPACE=$((DISK_SIZE_GB + 10)) # Add 10GB for Windows ISO and overhead - - # Check for KVM support - if [[ ! -e /dev/kvm ]]; then - gum style \ - --border normal \ - --padding "1 2" \ - --margin "1" \ - "❌ KVM virtualization not available!" \ - "" \ - "Please enable virtualization in BIOS or run:" \ - " sudo modprobe kvm-intel # for Intel CPUs" \ - " sudo modprobe kvm-amd # for AMD CPUs" - exit 1 - fi - - # Check disk space - AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}') - if (( AVAILABLE_SPACE < REQUIRED_SPACE )); then - echo "❌ Insufficient disk space!" - echo " Available: ${AVAILABLE_SPACE}GB" - echo " Required: ${REQUIRED_SPACE}GB (${DISK_SIZE_GB}GB disk + 10GB for Windows image)" - exit 1 - fi -} - -install_windows() { - # Set up trap to handle Ctrl+C - trap "echo ''; echo 'Installation cancelled by user'; exit 1" INT - - check_prerequisites - - nomarchy-pkg-add freerdp openbsd-netcat gum - - mkdir -p "$HOME/.windows" - mkdir -p "$HOME/.config/windows" - - cat << EOF | tee "$HOME/.local/share/applications/windows-vm.desktop" > /dev/null -[Desktop Entry] -Name=Windows -Comment=Start Windows VM via Docker and connect with RDP -Exec=uwsm app -- nomarchy-windows-vm launch -Icon=$HOME/.local/share/applications/icons/windows.png -Terminal=false -Type=Application -Categories=System;Virtualization; -EOF - - # Get system resources - TOTAL_RAM=$(free -h | awk 'NR==2 {print $2}') - TOTAL_RAM_GB=$(awk 'NR==1 {printf "%d", $2/1024/1024}' /proc/meminfo) - TOTAL_CORES=$(nproc) - - echo "" - echo "System Resources Detected:" - echo " Total RAM: $TOTAL_RAM" - echo " Total CPU Cores: $TOTAL_CORES" - echo "" - - RAM_OPTIONS="" - for size in 2 4 8 16 32 64; do - if (( size <= TOTAL_RAM_GB )); then - RAM_OPTIONS="$RAM_OPTIONS ${size}G" - fi - done - - SELECTED_RAM=$(echo $RAM_OPTIONS | tr ' ' '\n' | gum choose --selected="4G" --header="How much RAM would you like to allocate to Windows VM?") - - # Check if user cancelled - if [[ -z $SELECTED_RAM ]]; then - echo "Installation cancelled by user" - exit 1 - fi - - SELECTED_CORES=$(gum input --placeholder="Number of CPU cores (1-$TOTAL_CORES)" --value="2" --header="How many CPU cores would you like to allocate to Windows VM?" --char-limit=2) - - # Check if user cancelled (Ctrl+C in gum input returns empty string) - if [[ -z $SELECTED_CORES ]]; then - echo "Installation cancelled by user" - exit 1 - fi - - if ! [[ $SELECTED_CORES =~ ^[0-9]+$ ]] || (( SELECTED_CORES < 1 )) || (( SELECTED_CORES > TOTAL_CORES )); then - echo "Invalid input. Using default: 2 cores" - SELECTED_CORES=2 - fi - - AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}') - MAX_DISK_GB=$((AVAILABLE_SPACE - 10)) # Leave 10GB for Windows image - - # Check if we have enough space for minimum - if (( MAX_DISK_GB < 32 )); then - echo "❌ Insufficient disk space for Windows VM!" - echo " Available: ${AVAILABLE_SPACE}GB" - echo " Minimum required: 42GB (32GB disk + 10GB for Windows image)" - exit 1 - fi - - DISK_OPTIONS="" - for size in 32 64 128 256 512; do - if (( size <= MAX_DISK_GB )); then - DISK_OPTIONS="$DISK_OPTIONS ${size}G" - fi - done - - # Default to 64G if available, otherwise 32G - DEFAULT_DISK="64G" - if ! echo "$DISK_OPTIONS" | grep -q "64G"; then - DEFAULT_DISK="32G" - fi - - SELECTED_DISK=$(echo $DISK_OPTIONS | tr ' ' '\n' | gum choose --selected="$DEFAULT_DISK" --header="How much disk space would you like to give Windows VM? (64GB+ recommended)") - - # Check if user cancelled - if [[ -z $SELECTED_DISK ]]; then - echo "Installation cancelled by user" - exit 1 - fi - - # Extract just the number for prerequisite check - DISK_SIZE_NUM=$(echo "$SELECTED_DISK" | sed 's/G//') - - # Re-check prerequisites with selected disk size - check_prerequisites "$DISK_SIZE_NUM" - - # Prompt for username and password - USERNAME=$(gum input --placeholder="Username (Press enter to use default: docker)" --header="Enter Windows username:") - if [[ -z $USERNAME ]]; then - USERNAME="docker" - fi - - PASSWORD=$(gum input --placeholder="Password (Press enter to use default: admin)" --password --header="Enter Windows password:") - if [[ -z $PASSWORD ]]; then - PASSWORD="admin" - PASSWORD_DISPLAY="(default)" - else - PASSWORD_DISPLAY="(user-defined)" - fi - - # Display configuration summary - gum style \ - --border normal \ - --padding "1 2" \ - --margin "1" \ - --align left \ - --bold \ - "Windows VM Configuration" \ - "" \ - "RAM: $SELECTED_RAM" \ - "CPU: $SELECTED_CORES cores" \ - "Disk: $SELECTED_DISK" \ - "Username: $USERNAME" \ - "Password: $PASSWORD_DISPLAY" - - # Ask for confirmation - echo "" - if ! gum confirm "Proceed with this configuration?"; then - echo "Installation cancelled by user" - exit 1 - fi - - mkdir -p $HOME/Windows - - # Create docker-compose.yml in user config directory - cat << EOF | tee "$COMPOSE_FILE" > /dev/null -services: - windows: - image: dockurr/windows - container_name: nomarchy-windows - environment: - VERSION: "11" - RAM_SIZE: "$SELECTED_RAM" - CPU_CORES: "$SELECTED_CORES" - DISK_SIZE: "$SELECTED_DISK" - USERNAME: "$USERNAME" - PASSWORD: "$PASSWORD" - TZ: "$(timedatectl show -p Timezone --value 2>/dev/null || echo UTC)" - ARGUMENTS: "-rtc base=localtime,clock=host,driftfix=slew" - devices: - - /dev/kvm - - /dev/net/tun - cap_add: - - NET_ADMIN - ports: - - 127.0.0.1:8006:8006 - - 127.0.0.1:3389:3389/tcp - - 127.0.0.1:3389:3389/udp - volumes: - - $HOME/.windows:/storage - - $HOME/Windows:/shared - restart: unless-stopped - stop_grace_period: 2m -EOF - - echo "" - echo "Starting Windows VM installation..." - echo "This will download a Windows 11 image (may take 10-15 minutes)." - echo "" - echo "Monitor installation progress at: http://127.0.0.1:8006" - echo "" - - # Start docker-compose with user's config - echo "Starting Windows VM with docker-compose..." - if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then - echo "❌ Failed to start Windows VM!" - echo " Common issues:" - echo " - Docker daemon not running: sudo systemctl start docker" - echo " - Port already in use: check if another VM is running" - echo " - Permission issues: make sure you're in the docker group" - exit 1 - fi - - echo "" - echo "Windows VM is starting up!" - echo "" - echo "Opening browser to monitor installation..." - - # Open browser to monitor installation - sleep 3 - xdg-open "http://127.0.0.1:8006" - - echo "" - echo "Installation is running in the background." - echo "You can monitor progress at: http://127.0.0.1:8006" - echo "" - echo "Once finished, launch 'Windows' via Super + Space" - echo "" - echo "To stop the VM: nomarchy-windows-vm stop" - echo "To change resources: ~/.config/windows/docker-compose.yml" - echo "" -} - -remove_windows() { - if ! gum confirm --default=false "Remove Windows VM and delete all associated data?"; then - echo "Removal cancelled by user" - exit 1 - fi - - echo "Removing Windows VM..." - - docker-compose -f "$COMPOSE_FILE" down 2>/dev/null || true - - docker rmi dockurr/windows 2>/dev/null || echo "Image already removed or not found" - - rm "$HOME/.local/share/applications/windows-vm.desktop" - rm -rf "$HOME/.config/windows" - rm -rf "$HOME/.windows" - - echo "" - echo "Windows VM removal completed!" -} - -launch_windows() { - KEEP_ALIVE=false - if [[ $1 = "--keep-alive" ]] || [[ $1 = "-k" ]]; then - KEEP_ALIVE=true - fi - - # Check if config exists - if [[ ! -f $COMPOSE_FILE ]]; then - echo "Windows VM not configured. Please run: nomarchy-windows-vm install" - exit 1 - fi - - # Extract credentials from compose file - WIN_USER=$(grep "USERNAME:" "$COMPOSE_FILE" | sed 's/.*USERNAME: "\(.*\)"/\1/') - WIN_PASS=$(grep "PASSWORD:" "$COMPOSE_FILE" | sed 's/.*PASSWORD: "\(.*\)"/\1/') - - # Use defaults if not found - [[ -z $WIN_USER ]] && WIN_USER="docker" - [[ -z $WIN_PASS ]] && WIN_PASS="admin" - - # Check if container is already running - CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nomarchy-windows 2>/dev/null) - - if [[ $CONTAINER_STATUS != "running" ]]; then - echo "Starting Windows VM..." - - # Send desktop notification - notify-send " Starting Windows VM" " This can take 15-30 seconds" -t 15000 - - if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then - echo "❌ Failed to start Windows VM!" - echo " Try checking: nomarchy-windows-vm status" - echo " View logs: docker logs nomarchy-windows" - notify-send -u critical "Windows VM" "Failed to start Windows VM" - exit 1 - fi - - echo "Waiting for Windows VM to start..." - WAIT_COUNT=0 - until docker logs nomarchy-windows 2>&1 | grep -qi "windows started successfully"; do - sleep 2 - WAIT_COUNT=$((WAIT_COUNT + 1)) - if (( WAIT_COUNT > 60 )); then # 2 minutes timeout - echo "" - echo "❌ Timeout: Windows VM failed to start within 2 minutes" - echo " Check logs: docker logs nomarchy-windows" - exit 1 - fi - done - fi - - # Build the connection info - if [[ $KEEP_ALIVE = "true" ]]; then - LIFECYCLE="VM will keep running after RDP closes -To stop: nomarchy-windows-vm stop" - else - LIFECYCLE="VM will auto-stop when RDP closes" - fi - - gum style \ - --border normal \ - --padding "1 2" \ - --margin "1" \ - --align center \ - "Connecting to Windows VM" \ - "" \ - "$LIFECYCLE" - - # Detect display scale from Hyprland - HYPR_SCALE=$(hyprctl monitors -j | jq -r '.[] | select (.focused == true) | .scale') - SCALE_PERCENT=$(echo "$HYPR_SCALE" | awk '{print int($1 * 100)}') - - RDP_SCALE="" - if (( SCALE_PERCENT >= 170 )); then - RDP_SCALE="/scale:180" - elif (( SCALE_PERCENT >= 130 )); then - RDP_SCALE="/scale:140" - fi - # If scale is less than 130%, don't set any scale (use default 100) - - # Connect with RDP in fullscreen (auto-detects resolution) - xfreerdp3 /u:"$WIN_USER" /p:"$WIN_PASS" /v:127.0.0.1:3389 -grab-keyboard /sound /microphone /clipboard /cert:ignore /title:"Windows VM - Nomarchy" /dynamic-resolution /gfx:AVC444 /floatbar:sticky:off,default:visible,show:fullscreen $RDP_SCALE - - # After RDP closes, stop the container unless --keep-alive was specified - if [[ $KEEP_ALIVE = "false" ]]; then - echo "" - echo "RDP session closed. Stopping Windows VM..." - docker-compose -f "$COMPOSE_FILE" down - echo "Windows VM stopped." - else - echo "" - echo "RDP session closed. Windows VM is still running." - echo "To stop it: nomarchy-windows-vm stop" - fi -} - -stop_windows() { - if [[ ! -f $COMPOSE_FILE ]]; then - echo "Windows VM not configured." - exit 1 - fi - - echo "Stopping Windows VM..." - docker-compose -f "$COMPOSE_FILE" down - echo "Windows VM stopped." -} - -status_windows() { - if [[ ! -f $COMPOSE_FILE ]]; then - echo "Windows VM not configured." - echo "To set up: nomarchy-windows-vm install" - exit 1 - fi - - CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nomarchy-windows 2>/dev/null) - - if [[ -z $CONTAINER_STATUS ]]; then - echo "Windows VM container not found." - echo "To start: nomarchy-windows-vm launch" - elif [[ $CONTAINER_STATUS = "running" ]]; then - gum style \ - --border normal \ - --padding "1 2" \ - --margin "1" \ - --align left \ - "Windows VM Status: RUNNING" \ - "" \ - "Web interface: http://127.0.0.1:8006" \ - "RDP available: port 3389" \ - "" \ - "To connect: nomarchy-windows-vm launch" \ - "To stop: nomarchy-windows-vm stop" - else - echo "Windows VM is stopped (status: $CONTAINER_STATUS)" - echo "To start: nomarchy-windows-vm launch" - fi -} - -show_usage() { - echo "Usage: nomarchy-windows-vm [command] [options]" - echo "" - echo "Commands:" - echo " install Install and configure Windows VM" - echo " remove Remove Windows VM and optionally its data" - echo " launch [options] Start Windows VM (if needed) and connect via RDP" - echo " Options:" - echo " --keep-alive, -k Keep VM running after RDP closes" - echo " stop Stop the running Windows VM" - echo " status Show current VM status" - echo " help Show this help message" - echo "" - echo "Examples:" - echo " nomarchy-windows-vm install # Set up Windows VM for first time" - echo " nomarchy-windows-vm launch # Connect to VM (auto-stop on exit)" - echo " nomarchy-windows-vm launch -k # Connect to VM (keep running)" - echo " nomarchy-windows-vm stop # Shut down the VM" -} - -# Main command dispatcher -case "$1" in - install) - install_windows - ;; - remove) - remove_windows - ;; - launch|start) - launch_windows "$2" - ;; - stop|down) - stop_windows - ;; - status) - status_windows - ;; - help|--help|-h|"") - show_usage - ;; - *) - echo "Unknown command: $1" >&2 - echo "" >&2 - show_usage >&2 - exit 1 - ;; -esac diff --git a/flake.lock b/flake.lock index f632b92..075584c 100644 --- a/flake.lock +++ b/flake.lock @@ -38,11 +38,11 @@ "base16-helix": { "flake": false, "locked": { - "lastModified": 1760703920, - "narHash": "sha256-m82fGUYns4uHd+ZTdoLX2vlHikzwzdu2s2rYM2bNwzw=", + "lastModified": 1776754714, + "narHash": "sha256-E3OAK27smtATTmX45uoTSRsVD+Y+ZiVVfgM/tjpbtYg=", "owner": "tinted-theming", "repo": "base16-helix", - "rev": "d646af9b7d14bff08824538164af99d0c521b185", + "rev": "4d508123037e7851ad36ebf7d9c48b0e9e1eb581", "type": "github" }, "original": { @@ -51,22 +51,6 @@ "type": "github" } }, - "base16-schemes": { - "flake": false, - "locked": { - "lastModified": 1696158499, - "narHash": "sha256-5yIHgDTPjoX/3oDEfLSQ0eJZdFL1SaCfb9d6M0RmOTM=", - "owner": "tinted-theming", - "repo": "base16-schemes", - "rev": "a9112eaae86d9dd8ee6bb9445b664fba2f94037a", - "type": "github" - }, - "original": { - "owner": "tinted-theming", - "repo": "base16-schemes", - "type": "github" - } - }, "base16-vim": { "flake": false, "locked": { @@ -84,59 +68,14 @@ "type": "github" } }, - "disko": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1773889306, - "narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=", - "owner": "nix-community", - "repo": "disko", - "rev": "5ad85c82cc52264f4beddc934ba57f3789f28347", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "disko", - "type": "github" - } - }, - "elephant": { - "inputs": { - "nixpkgs": [ - "walker", - "nixpkgs" - ], - "systems": [ - "walker", - "systems" - ] - }, - "locked": { - "lastModified": 1775706155, - "narHash": "sha256-h7Rw0vlb0n0Jsk21WJPm7H+1T1bG+PEuxE5cJ2TZl8A=", - "owner": "abenz1267", - "repo": "elephant", - "rev": "376ee71c66db38683daabd57350bf3f6f086eaf8", - "type": "github" - }, - "original": { - "owner": "abenz1267", - "repo": "elephant", - "type": "github" - } - }, "firefox-gnome-theme": { "flake": false, "locked": { - "lastModified": 1775176642, - "narHash": "sha256-2veEED0Fg7Fsh81tvVDNYR6SzjqQxa7hbi18Jv4LWpM=", + "lastModified": 1779670703, + "narHash": "sha256-UdfMivNMwCCqQsYDg5pSz8X2IOaOrIZLIIy+Bg3CO2o=", "owner": "rafaelmardojai", "repo": "firefox-gnome-theme", - "rev": "179704030c5286c729b5b0522037d1d51341022c", + "rev": "942159e73e40bf785816f7f1f5feed9ef3d7c8f9", "type": "github" }, "original": { @@ -153,11 +92,11 @@ ] }, "locked": { - "lastModified": 1775087534, - "narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", + "lastModified": 1778716662, + "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", + "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", "type": "github" }, "original": { @@ -206,127 +145,57 @@ ] }, "locked": { - "lastModified": 1775425411, - "narHash": "sha256-KY6HsebJHEe5nHOWP7ur09mb0drGxYSzE3rQxy62rJo=", + "lastModified": 1781063120, + "narHash": "sha256-1UIF/mDJluwJQjmmcZ2j1L2+mjYsefe82QCLj0TYSOg=", "owner": "nix-community", "repo": "home-manager", - "rev": "0d02ec1d0a05f88ef9e74b516842900c41f0f2fe", + "rev": "baa46aeb6d02e0ba13de67cd35e3d57aedfacf01", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-25.11", + "ref": "release-26.05", "repo": "home-manager", "type": "github" } }, - "home-manager_2": { - "inputs": { - "nixpkgs": [ - "impermanence", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1768598210, - "narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=", - "owner": "nix-community", - "repo": "home-manager", - "rev": "c47b2cc64a629f8e075de52e4742de688f930dc6", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "home-manager", - "type": "github" - } - }, - "impermanence": { - "inputs": { - "home-manager": "home-manager_2", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1769548169, - "narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=", - "owner": "nix-community", - "repo": "impermanence", - "rev": "7b1d382faf603b6d264f58627330f9faa5cba149", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "impermanence", - "type": "github" - } - }, - "nix-colors": { - "inputs": { - "base16-schemes": "base16-schemes", - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1707825078, - "narHash": "sha256-hTfge2J2W+42SZ7VHXkf4kjU+qzFqPeC9k66jAUBMHk=", - "owner": "misterio77", - "repo": "nix-colors", - "rev": "b01f024090d2c4fc3152cd0cf12027a7b8453ba1", - "type": "github" - }, - "original": { - "owner": "misterio77", - "repo": "nix-colors", - "type": "github" - } - }, "nixos-hardware": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, "locked": { - "lastModified": 1775490113, - "narHash": "sha256-2ZBhDNZZwYkRmefK5XLOusCJHnoeKkoN95hoSGgMxWM=", + "lastModified": 1781020964, + "narHash": "sha256-fS7xTi2j2iso5Hj7RNZLv/acDlCT+fgMVkVk40A7Uco=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "c775c2772ba56e906cbeb4e0b2db19079ef11ff7", + "rev": "32c2cd9e46286c4eced3dc6b613c659126bf3cca", "type": "github" }, "original": { "owner": "NixOS", + "ref": "master", "repo": "nixos-hardware", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1775811116, - "narHash": "sha256-t+HZK42pB6N+i5RGbuy7Xluez/VvWbembBdvzsc23Ss=", - "owner": "nixos", + "lastModified": 1780902259, + "narHash": "sha256-q8yYEC5f1mFlQO9RGna4LTc9QrcvWunX6FYp83munkQ=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "54170c54449ea4d6725efd30d719c5e505f1c10e", + "rev": "bd0ff2d3eac24699c3664d5966b9ef36f388e2ca", "type": "github" }, "original": { - "owner": "nixos", - "ref": "nixos-25.11", + "owner": "NixOS", + "ref": "nixos-26.05", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1697935651, - "narHash": "sha256-qOfWjQ2JQSQL15KLh6D7xQhx0qgZlYZTYlcEiRuAMMw=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "e1e11fdbb01113d85c7f41cada9d2847660e3902", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, "nur": { "inputs": { "flake-parts": [ @@ -339,11 +208,11 @@ ] }, "locked": { - "lastModified": 1775228139, - "narHash": "sha256-ebbeHmg+V7w8050bwQOuhmQHoLOEOfqKzM1KgCTexK4=", + "lastModified": 1780281641, + "narHash": "sha256-M/+hUKoKbHXpV0xGVfELbN1Ds1aoe3pL5p5/t46YhVo=", "owner": "nix-community", "repo": "NUR", - "rev": "601971b9c89e0304561977f2c28fa25e73aa7132", + "rev": "30f9ae2f04174de63ba8bcf3580ca90843b28a01", "type": "github" }, "original": { @@ -354,14 +223,10 @@ }, "root": { "inputs": { - "disko": "disko", "home-manager": "home-manager", - "impermanence": "impermanence", - "nix-colors": "nix-colors", "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs", - "stylix": "stylix", - "walker": "walker" + "stylix": "stylix" } }, "stylix": { @@ -384,11 +249,11 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1775927784, - "narHash": "sha256-9Gm33hvgcy4hR7O9EjWrw2kipyVBIHgNUtAHyWgLDhg=", + "lastModified": 1781018772, + "narHash": "sha256-C+cGIUaC6dqfwTbI+BwCd572PbESGA3WYxR1sLTqxkY=", "owner": "danth", "repo": "stylix", - "rev": "8f6e6b3844b3b0b59470e276bd3fe9ab04600081", + "rev": "a378e4c09031fb15a4d65da88aa628f71fc52f6b", "type": "github" }, "original": { @@ -412,21 +277,6 @@ "type": "github" } }, - "systems_2": { - "locked": { - "lastModified": 1689347949, - "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", - "owner": "nix-systems", - "repo": "default-linux", - "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default-linux", - "type": "github" - } - }, "tinted-kitty": { "flake": false, "locked": { @@ -446,11 +296,11 @@ "tinted-schemes": { "flake": false, "locked": { - "lastModified": 1772661346, - "narHash": "sha256-4eu3LqB9tPqe0Vaqxd4wkZiBbthLbpb7llcoE/p5HT0=", + "lastModified": 1777806186, + "narHash": "sha256-PDF0/wObw4nIsSBeXVYLsloXOiphXCgIdsrNcVXguKs=", "owner": "tinted-theming", "repo": "schemes", - "rev": "13b5b0c299982bb361039601e2d72587d6846294", + "rev": "0c94645546f4f3ddac77a1a5fce54eb95bf50795", "type": "github" }, "original": { @@ -462,11 +312,11 @@ "tinted-tmux": { "flake": false, "locked": { - "lastModified": 1772934010, - "narHash": "sha256-x+6+4UvaG+RBRQ6UaX+o6DjEg28u4eqhVRM9kpgJGjQ=", + "lastModified": 1778379944, + "narHash": "sha256-wPDFzMGSlARlw0Sfsn48Q2+jPSfk6N0Ng6BC/d+7Q24=", "owner": "tinted-theming", "repo": "tinted-tmux", - "rev": "c3529673a5ab6e1b6830f618c45d9ce1bcdd829d", + "rev": "fe0203a198690e71a5ff11e08812a4673de3678d", "type": "github" }, "original": { @@ -478,11 +328,11 @@ "tinted-zed": { "flake": false, "locked": { - "lastModified": 1772909925, - "narHash": "sha256-jx/5+pgYR0noHa3hk2esin18VMbnPSvWPL5bBjfTIAU=", + "lastModified": 1778378178, + "narHash": "sha256-OXPXRIQgGwV77HjYRryOHguh4ALX96jkg+tseLkGgHA=", "owner": "tinted-theming", "repo": "base16-zed", - "rev": "b4d3a1b3bcbd090937ef609a0a3b37237af974df", + "rev": "9cd816033ff969415b190722cddf134e78a5665f", "type": "github" }, "original": { @@ -490,28 +340,6 @@ "repo": "base16-zed", "type": "github" } - }, - "walker": { - "inputs": { - "elephant": "elephant", - "nixpkgs": [ - "nixpkgs" - ], - "systems": "systems_2" - }, - "locked": { - "lastModified": 1775815794, - "narHash": "sha256-4aYljsM+f09YLERdo+kQjg9lvxVc6yxc+rPRFdgeXxw=", - "owner": "abenz1267", - "repo": "walker", - "rev": "a0b5fa92e05c7d52936472c5027265b14e746bae", - "type": "github" - }, - "original": { - "owner": "abenz1267", - "repo": "walker", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 6fb14d8..11862e5 100644 --- a/flake.nix +++ b/flake.nix @@ -1,306 +1,322 @@ { - description = "Nomarchy - A NixOS-based distribution with Omarchy flavour"; + description = "Nomarchy — the rock-solid reproducibility of NixOS, the polish of an opinionated desktop"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; - nixos-hardware.url = "github:NixOS/nixos-hardware"; - - disko = { - url = "github:nix-community/disko"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; + + home-manager = { + url = "github:nix-community/home-manager/release-26.05"; inputs.nixpkgs.follows = "nixpkgs"; }; - impermanence = { - url = "github:nix-community/impermanence"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - home-manager = { - url = "github:nix-community/home-manager/release-25.11"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - # nix-colors is a pure data flake (base16 schemes) — it has no inputs, - # so any `inputs.X.follows` declaration here triggers a "non-existent - # input" warning. Just URL it. - nix-colors.url = "github:misterio77/nix-colors"; stylix = { url = "github:danth/stylix"; inputs.nixpkgs.follows = "nixpkgs"; + inputs.home-manager.follows = "home-manager"; }; - walker = { - url = "github:abenz1267/walker"; + + # Pinned by Nomarchy so distro + hardware quirks are tested together + # (consumed by lib.mkFlake's hardwareProfile). Its nixpkgs input is + # only used by its own CI — follow ours to keep locks/ISO lean. + nixos-hardware = { + url = "github:NixOS/nixos-hardware/master"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nixpkgs, nixos-hardware, disko, impermanence, home-manager, nix-colors, stylix, walker, ... } @ inputs: let - system = "x86_64-linux"; - lib = nixpkgs.lib; + outputs = { self, nixpkgs, home-manager, stylix, nixos-hardware, ... }: + let + system = "x86_64-linux"; + username = "nomarchy"; # reference host only; downstream picks its own - # Shared Nomarchy helpers (palette loader, theme name list, wallpaper / - # icon-theme resolvers, base16 mapper). Importing here makes lib/default.nix - # the single source of truth — modules that need palette data import - # `../../lib` the same way and resolve to the same evaluation. - nomarchyLib = import ./lib { inherit lib; }; + pkgs = import nixpkgs { + inherit system; + overlays = [ self.overlays.default ]; + }; - # Overlays — the function form lives here so both Nomarchy itself and - # any downstream flake (see `overlays.default` in the outputs) can layer - # the same Nomarchy packages onto their nixpkgs. - nomarchyOverlay = final: prev: { - makima-bin = prev.makima; - nomarchy-system-scripts = final.callPackage ./core/system/scripts-derivation.nix { }; - }; - overlays = [ nomarchyOverlay ]; - - pkgs = import nixpkgs { - inherit system overlays; - config.allowUnfree = true; - }; - - # Helper to create standalone home configurations - mkHome = { username, modules ? [] }: home-manager.lib.homeManagerConfiguration { - inherit pkgs; - extraSpecialArgs = { inherit inputs; }; - modules = [ - ./features - { - home.username = username; - home.homeDirectory = "/home/${username}"; - home.stateVersion = "25.11"; - } - ] ++ modules; - }; - - homeConfigs = { - "nixos" = mkHome { username = "nixos"; }; - "nomarchy" = mkHome { username = "nomarchy"; }; - }; - - # Realise the full HM generation for every palette so theme switches hit - # the Nix cache instead of rebuilding Stylix outputs per swap. Each entry - # pins `nomarchy.theme` (and clears the wallpaper override so the theme's - # own default background is used) and exposes the generation's activation - # package under its theme name. - inherit (nomarchyLib) themeNames; - themeVariantFor = themeName: (mkHome { - username = "nomarchy"; - modules = [{ - nomarchy.theme = lib.mkForce themeName; - nomarchy.wallpaper = lib.mkForce ""; - nomarchy.apps = { - alacritty.enable = true; - btop.enable = true; - elephant.enable = true; - swayosd.enable = true; - walker.enable = true; - vscode.enable = true; - kitty.enable = true; - lazygit.enable = true; - tmux.enable = true; + # The guided installer — live-ISO only (not in the overlay): it bakes + # in the downstream template, this checkout's flake.lock, and the + # source identity of this very revision so installs work offline. + gitUrl = "https://git.bemagri.xyz/bernardo/nomarchy.git"; + nomarchyInstall = pkgs.callPackage ./pkgs/nomarchy-install { + templateDir = ./templates/downstream; + nomarchyLock = ./flake.lock; + hardwareModuleNames = pkgs.writeText "nixos-hardware-modules.txt" + (nixpkgs.lib.concatStringsSep "\n" + (builtins.attrNames nixos-hardware.nixosModules)); + nixpkgsPath = nixpkgs.outPath; + # v1 is the release branch: installed machines track it on + # `nix flake update` instead of the forge's default branch. + flakeUrl = "git+${gitUrl}?ref=v1"; + # Future updates re-resolve from the forge (original); the install + # itself resolves from the ISO store (locked = path) — fully + # offline, even from a dirty-tree ISO. + originalNode = { type = "git"; url = gitUrl; ref = "v1"; }; + lockedNode = { + type = "path"; + path = self.outPath; + narHash = self.narHash; + lastModified = self.lastModified or 0; }; - home.packages = with pkgs; [ - firefox - xfce.thunar - imv - mpv - fastfetch - chromium - ]; - }]; - }).activationPackage; - allThemeVariants = pkgs.linkFarm "nomarchy-all-theme-variants" - (map (name: { inherit name; path = themeVariantFor name; }) themeNames); - in { - # Expose inputs for downstream use - inherit inputs; + rev = self.rev or null; + }; + in + { + # ─── Downstream API ──────────────────────────────────────────── + # A user's machine flake imports these and layers its own + # system.nix / home.nix on top (see templates/downstream). + # Their theme-state.json lives in THEIR flake and is wired up via + # the `nomarchy.stateFile` option — reading it stays pure. - # Downstream flakes consume this to layer Nomarchy's packages onto their - # own nixpkgs. Used by the installer-generated flake so both the - # nixosSystem and the standalone homeManagerConfiguration share one - # `pkgs` with the overlay applied. - overlays.default = nomarchyOverlay; + overlays.default = final: prev: { + nomarchy-theme-sync = final.callPackage ./pkgs/nomarchy-theme-sync { + # Presets baked into the package, so `nomarchy-theme-sync list` + # works on machines that don't check out this repo. + themesDir = ./themes; + }; + }; - nixosModules = { - system = import ./core; - home = import ./features; - }; + nixosModules.nomarchy = { + imports = [ ./modules/nixos ]; + nixpkgs.overlays = [ self.overlays.default ]; + }; - nixosConfigurations = { - # Minimal TTY installer ISO (new golden path) - nomarchy-installer = nixpkgs.lib.nixosSystem { - specialArgs = { inherit inputs; }; + homeModules.nomarchy = { + # stylix's home module is injected here because it needs the + # flake input; modules/home/stylix.nix only configures it. + imports = [ stylix.homeModules.stylix ./modules/home ]; + }; + + # One-call downstream wrapper — see templates/downstream/flake.nix. + # The generated flake is never hand-edited; machines live in + # system.nix / home.nix. + lib = import ./lib.nix { + inherit nixpkgs home-manager nixos-hardware; + nomarchy = self; + }; + + templates.default = { + path = ./templates/downstream; + description = "Nomarchy machine configuration (downstream flake)"; + }; + + packages.${system} = { + nomarchy-theme-sync = pkgs.nomarchy-theme-sync; + nomarchy-install = nomarchyInstall; + default = pkgs.nomarchy-theme-sync; + }; + + # ─── Checks ──────────────────────────────────────────────────── + # `nix flake check` never evaluates templates/, so exercise the + # downstream template through mkFlake directly (this bypasses the + # template's own three-line flake.nix, which only resolves once + # instantiated — everything else is covered here, including the + # hardwareProfile → nixos-hardware mapping). + checks.${system} = + let + downstream = self.lib.mkFlake { + src = ./templates/downstream; + username = "me"; + hardwareProfile = "framework-13-7040-amd"; # exercises the mapping + }; + in + { + downstream-template-system = + downstream.nixosConfigurations.default.config.system.build.toplevel; + downstream-template-home = + downstream.homeConfigurations.me.activationPackage; + }; + + # ─── Reference host ──────────────────────────────────────────── + # Manually wired rather than via lib.mkFlake: the repo-root + # theme-state.json is load-bearing (live ISO + in-repo CLI dev), + # which doesn't match mkFlake's src-layout convention. + # Deliberately split rebuild paths: + # system → sudo nixos-rebuild switch --flake .#nomarchy (rare) + # desktop → home-manager switch --flake .#nomarchy (no sudo; + # this is what `nomarchy-theme-sync apply` runs) + # Theme changes never touch the system layer, so they never need + # root and never rebuild the world. + nixosConfigurations.nomarchy = nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { inherit username; }; modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - nixpkgs.overlays = overlays; + self.nixosModules.nomarchy + ./hosts/default/configuration.nix + ]; + }; + + homeConfigurations.${username} = home-manager.lib.homeManagerConfiguration { + inherit pkgs; + modules = [ + self.homeModules.nomarchy + { + # The single source of truth — pure read: the file is part + # of this flake's source. + nomarchy.stateFile = ./theme-state.json; + home = { + inherit username; + homeDirectory = "/home/${username}"; + }; } + ]; + }; + + # ─── Live ISO ────────────────────────────────────────────────── + # Full desktop on a bootable stick, no installation: + # nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage + # Test in QEMU with tools/test-live-iso.sh. See docs/TESTING.md. + nixosConfigurations.nomarchy-live = nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { inherit username; nomarchySrc = self; }; + modules = [ "${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" - ./hosts/nomarchy-installer.nix - { - system.stateVersion = "25.11"; - networking.hostName = "nomarchy-installer"; - } - ]; - }; + self.nixosModules.nomarchy + ./hosts/live.nix - # Installer VM for testing - installerVm = nixpkgs.lib.nixosSystem { - specialArgs = { inherit inputs; }; - modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - nixpkgs.overlays = overlays; - } - "${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" - ./hosts/nomarchy-installer.nix - { - system.stateVersion = "25.11"; - networking.hostName = "nomarchy-installer-vm"; - virtualisation.vmVariant.virtualisation.memorySize = 4096; - virtualisation.vmVariant.virtualisation.cores = 2; - } - ]; - }; - - # Graphical installer ISO (legacy, for users who prefer GUI) - nomarchy-live = nixpkgs.lib.nixosSystem { - specialArgs = { inherit inputs; }; - modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - nixpkgs.overlays = overlays; - } - "${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix" + # The ISO is a sealed artifact, so the desktop is baked in via + # the HM NixOS module (the installed-system split into + # nixos-rebuild vs home-manager switch doesn't apply here — + # though the seeded ~/.nomarchy flake still allows standalone + # `home-manager switch` for theme testing). home-manager.nixosModules.home-manager - ./hosts/nomarchy-live.nix - ./core { - system.stateVersion = "25.11"; - networking.hostName = "nomarchy-live"; - services.displayManager.autoLogin.enable = true; - services.displayManager.autoLogin.user = "nixos"; - - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - home-manager.backupFileExtension = "hm-bak"; - home-manager.extraSpecialArgs = { inherit inputs; }; - home-manager.users.nixos = { - imports = [ ./features ]; - home.stateVersion = "25.11"; - nomarchy.apps = { - alacritty.enable = true; - btop.enable = true; - elephant.enable = true; - swayosd.enable = true; - walker.enable = true; - vscode.enable = true; - kitty.enable = true; - lazygit.enable = true; - tmux.enable = true; + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + users.${username} = { + imports = [ self.homeModules.nomarchy ]; + nomarchy.stateFile = ./theme-state.json; }; - home.packages = with pkgs; [ - firefox - xfce.thunar - imv - mpv - fastfetch - chromium - ]; - - # 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. - wayland.windowManager.hyprland.extraConfig = nixpkgs.lib.mkAfter '' - 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"' - ''; - }; - } - ]; - }; - - # Default configuration (VM and testing) - default = nixpkgs.lib.nixosSystem { - specialArgs = { inherit inputs; }; - modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - nixpkgs.overlays = overlays; - } - home-manager.nixosModules.home-manager - ./core/default.nix - ./core/system/vm-guest.nix - { - system.stateVersion = "25.11"; - networking.hostName = "nomarchy"; - - virtualisation.vmVariant.virtualisation.memorySize = 4096; - virtualisation.vmVariant.virtualisation.cores = 2; - - # Setup default user for testing - users.users.nomarchy = { - isNormalUser = true; - extraGroups = [ "wheel" "video" "render" "networkmanager" ]; - initialPassword = "nixos"; }; - services.displayManager.autoLogin.enable = true; - services.displayManager.autoLogin.user = "nomarchy"; + # The standalone CLI that `nomarchy-theme-sync apply` runs for + # theme switching, from the same pinned input — plus the + # guided installer. + environment.systemPackages = [ + home-manager.packages.${system}.home-manager + nomarchyInstall + ]; - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - home-manager.backupFileExtension = "hm-bak"; - home-manager.extraSpecialArgs = { inherit inputs; }; - home-manager.users.nomarchy = { - imports = [ ./features ]; - home.stateVersion = "25.11"; - nomarchy.apps = { - alacritty.enable = true; - btop.enable = true; - elephant.enable = true; - swayosd.enable = true; - walker.enable = true; - vscode.enable = true; - kitty.enable = true; - lazygit.enable = true; - tmux.enable = true; - }; - home.packages = with pkgs; [ - firefox - xfce.thunar - imv - mpv - fastfetch - chromium - ]; - }; + # Keep the locked flake inputs in the ISO store so theme + # switching on the live system works without a network: the + # lockfile's narHashes resolve against these paths instead + # of fetching from GitHub. Must be *transitive* — evaluating + # the seeded flake also fetches stylix's own inputs. + system.extraDependencies = + let + collectInputs = input: + [ input.outPath ] ++ builtins.concatMap collectInputs + (builtins.attrValues (input.inputs or { })); + in + collectInputs self ++ ( + # Pre-build a system shaped like what nomarchy-install + # GENERATES (snapper, auto-login, LUKS initrd, swapfile + + # resume — not the bare template!) plus the template + # desktop, so the offline install never builds packages + # from source: a custom username/hostname then changes + # only a handful of derivations. No hardware profile — + # matches a VM install; profiled installs share most of it. + let + template = self.lib.mkFlake { + src = ./templates/downstream; + username = username; + }; + representativeInstall = nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { inherit username; }; + modules = [ + self.nixosModules.nomarchy + { environment.systemPackages = [ home-manager.packages.${system}.home-manager ]; } + # The installer's autodetection emits these on real + # machines (and the intel CPU module pulls the whole + # iGPU stack) — without them an offline install on + # common hardware builds graphics drivers from + # source. Model-specific profiles may still need + # network; the common ground is covered. + nixos-hardware.nixosModules.common-cpu-intel + nixos-hardware.nixosModules.common-cpu-amd + nixos-hardware.nixosModules.common-gpu-amd + nixos-hardware.nixosModules.common-pc-laptop + nixos-hardware.nixosModules.common-pc-ssd + nixos-hardware.nixosModules.common-pc + (./templates/downstream + "/hardware-configuration.nix") + (./templates/downstream + "/system.nix") + ({ lib, ... }: { + nomarchy.system.snapper.enable = true; + nomarchy.system.greeter.autoLogin = username; + swapDevices = [{ device = "/swap/swapfile"; }]; + boot.resumeDevice = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000"; + boot.kernelParams = [ "resume_offset=1" ]; + boot.initrd.luks.devices.crypted.device = "/dev/disk/by-partlabel/disk-main-root"; + # The real install is BTRFS with a vfat ESP — the + # placeholder's bare ext4 would leave fs-gated bits + # out of this pin (snapper via btrfs; mtools and + # dosfstools enter systemPackages via vfat!). + fileSystems."/" = lib.mkForce { + device = "/dev/mapper/crypted"; + fsType = "btrfs"; + options = [ "subvol=@" "compress=zstd" "noatime" ]; + }; + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/0000-0000"; + fsType = "vfat"; + options = [ "umask=0077" ]; + }; + }) + ]; + }; + in [ + representativeInstall.config.system.build.toplevel + template.homeConfigurations.${username}.activationPackage + + # A real install rebuilds the drvs that embed the + # user's hostname/username/UUIDs (etc, initrd, units, + # toplevel, HM activation). inputDerivation pins each + # one's direct build inputs… + representativeInstall.config.system.build.toplevel.inputDerivation + representativeInstall.config.system.build.etc.inputDerivation + representativeInstall.config.system.build.initialRamdisk.inputDerivation + template.homeConfigurations.${username}.activationPackage.inputDerivation + ] ++ [ + # …and the second-level build tools those rebuilds need + # (their parents are themselves rebuilt, so parent + # inputDerivations don't reach them): kernel-module + # shrinking, unit-dir assembly, dbus config, /etc. + pkgs.kmod.dev + pkgs.nukeReferences + pkgs.xorg.lndir + pkgs.libxslt.bin + pkgs.python3Minimal + # system-path is rebuilt on EVERY machine: buildEnv's + # package-list order follows module-graph order, and any + # hardware profile/import reshuffles it. Its builder + # wants these. + pkgs.texinfo + pkgs.getconf + # greetd.toml embeds the auto-login user and is generated + # by remarshal — without this pin an offline install + # tries to BUILD remarshal, whose test closure pulls + # matplotlib/ffmpeg/x265 (the ~1900-drv cascade). + pkgs.remarshal + + # …and one representative disko script: its tool closure + # (file, which, wrapper hooks, …) isn't otherwise on the + # ISO, and offline nix would try to build it from source. + # The user's actual args produce a different script drv, + # but with identical inputs — built offline in seconds. + (import "${pkgs.disko}/share/disko/cli.nix" { + inherit pkgs; + mode = "destroy,format,mount"; + diskoFile = ./pkgs/nomarchy-install/disko-config.nix; + mainDrive = "/dev/vda"; + withLuks = true; + swapSize = "2G"; + }) + ] + ); } ]; }; }; - - homeConfigurations = homeConfigs; - - packages.${system} = { - # Pre-cache every theme's Home Manager generation so subsequent - # `nomarchy-theme-set` swaps are cache hits (no Stylix rebuild, no - # downloads). Run once after install: - # nix build /etc/nomarchy#allThemeVariants --no-link - inherit allThemeVariants; - default = allThemeVariants; - - # Expose installer VM as a package - installerVm = self.nixosConfigurations.installerVm.config.system.build.vm; - }; - - apps.${system} = { - installerVm = { - type = "app"; - program = "${self.packages.${system} .installerVm}/bin/run-nomarchy-installer-vm"; - }; - }; - }; } diff --git a/hosts/default/configuration.nix b/hosts/default/configuration.nix new file mode 100644 index 0000000..055d484 --- /dev/null +++ b/hosts/default/configuration.nix @@ -0,0 +1,24 @@ +# Reference host — machine specifics ONLY. +# The distro itself comes from nixosModules.nomarchy (modules/nixos), +# imported in flake.nix. Keep this file boring. +{ pkgs, username, ... }: + +{ + imports = [ ./hardware-configuration.nix ]; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + boot.kernelPackages = pkgs.linuxPackages_latest; + + networking.hostName = "nomarchy"; + time.timeZone = "UTC"; # adjust per machine + i18n.defaultLocale = "en_US.UTF-8"; + + users.users.${username} = { + isNormalUser = true; + description = "Nomarchy user"; + extraGroups = [ "wheel" "networkmanager" "video" "input" ]; + }; + + system.stateVersion = "26.05"; +} diff --git a/hosts/default/hardware-configuration.nix b/hosts/default/hardware-configuration.nix new file mode 100644 index 0000000..5691209 --- /dev/null +++ b/hosts/default/hardware-configuration.nix @@ -0,0 +1,15 @@ +# PLACEHOLDER — replace with the output of `nixos-generate-config` on the +# target machine. The mkDefault values below only exist so the flake +# evaluates and `nix flake check` passes before first install. +{ lib, ... }: + +{ + boot.initrd.availableKernelModules = lib.mkDefault [ "xhci_pci" "ahci" "nvme" "usbhid" "sd_mod" ]; + + fileSystems."/" = lib.mkDefault { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/hosts/live.nix b/hosts/live.nix new file mode 100644 index 0000000..786a0bc --- /dev/null +++ b/hosts/live.nix @@ -0,0 +1,86 @@ +# Live ISO host — boot the full Nomarchy desktop from a USB stick or QEMU +# without touching the disk. No installer yet (see roadmap); this target +# exists to test the distro end-to-end on real hardware. +{ lib, pkgs, username, nomarchySrc, ... }: + +{ + networking.hostName = "nomarchy-live"; + + isoImage.volumeID = lib.mkForce "NOMARCHY_LIVE"; + isoImage.edition = lib.mkForce "live"; + + # The minimal-CD profile enables wpa_supplicant; Nomarchy ships + # NetworkManager and the two must not run together. + networking.wireless.enable = lib.mkForce false; + + # ── Live user: no password, straight into the desktop ────────────── + users.users.${username} = { + isNormalUser = true; + initialHashedPassword = ""; + extraGroups = [ "wheel" "networkmanager" "video" "render" "audio" "input" ]; + }; + security.sudo.wheelNeedsPassword = false; + services.getty.autologinUser = lib.mkForce username; + + # Boot straight into Hyprland once; logging out lands on tuigreet. + services.greetd.settings.initial_session = { + command = "Hyprland"; + user = username; + }; + + # ── Hardware breadth ──────────────────────────────────────────────── + # Force-loading every GPU driver in the initrd (amdgpu+radeon+nouveau+ + # i915) panics most machines — only one of them can claim the GPU and + # the others explode. `availableKernelModules` lets udev load just the + # one that matches; virtio_gpu covers QEMU (tools/test-live-iso.sh). + boot.initrd.availableKernelModules = [ "amdgpu" "radeon" "nouveau" "i915" "virtio_gpu" ]; + services.qemuGuest.enable = lib.mkDefault true; + + # ── The Nomarchy flake on board ───────────────────────────────────── + # Read-only copy in /etc (also pins the flake source into the ISO + # closure); seeded writable into the live home so theme switching — + # state write + `home-manager switch` — works exactly like on an + # installed system. $NOMARCHY_PATH already defaults to ~/.nomarchy. + environment.etc."nomarchy".source = nomarchySrc; + + systemd.services.nomarchy-seed-flake = { + description = "Seed a writable Nomarchy flake into the live user's home"; + wantedBy = [ "multi-user.target" ]; + serviceConfig.Type = "oneshot"; + script = '' + home=/home/${username} + if [ ! -e "$home/.nomarchy" ]; then + cp -r ${nomarchySrc} "$home/.nomarchy" + chmod -R u+w "$home/.nomarchy" + chown -R ${username}:users "$home/.nomarchy" + fi + ''; + }; + + services.getty.helpLine = lib.mkForce '' + + Welcome to the Nomarchy live environment. + + The graphical session autologins as '${username}' (no password). + Theme switching: nomarchy-theme-sync apply <name> (or SUPER+T) + Wallpapers: nomarchy-theme-sync bg next (or SUPER+SHIFT+T) + Install to disk: nomarchy-install + The flake lives at ~/.nomarchy. + ''; + + # ── Live-session desktop tweaks ───────────────────────────────────── + home-manager.users.${username} = { + wayland.windowManager.hyprland.settings = { + # QEMU (and some panels) report a tiny "preferred" mode; ask for + # the highest resolution instead. + monitor = lib.mkForce [ ",highres,auto,1" ]; + # Welcome toast once the session is up (concatenated onto the + # base exec-once list). + exec-once = [ + "sh -c 'sleep 3; notify-send -a Nomarchy \"Welcome to Nomarchy\" \"SUPER+Return terminal · SUPER+T themes · install with nomarchy-install\"'" + ]; + }; + }; + + system.stateVersion = "26.05"; +} diff --git a/hosts/nomarchy-installer.nix b/hosts/nomarchy-installer.nix deleted file mode 100644 index 025d7dd..0000000 --- a/hosts/nomarchy-installer.nix +++ /dev/null @@ -1,122 +0,0 @@ -{ config, pkgs, inputs, lib, ... }: - -# Minimal TTY Installer ISO Configuration -# -# This creates a minimal, text-only installation environment. -# No desktop environment - just TTY with gum-based installer. - -{ - imports = [ - ../core/system/nix.nix - ../core/system/branding.nix - ]; - - # Base installation media configuration is handled by the module imported in flake.nix - - # Console configuration for a pleasant TTY experience. - # keyMap and i18n.defaultLocale are mkDefault — the install.sh keymap step - # calls `loadkeys` at runtime to honor the user's pick during the install, - # and writes the chosen value into the *installed* system's system.nix. - console = { - font = "ter-v16n"; - packages = [ pkgs.terminus_font ]; - keyMap = lib.mkDefault "us"; - }; - - i18n.defaultLocale = lib.mkDefault "en_US.UTF-8"; - - # ISO Branding - isoImage.volumeID = lib.mkForce "NOMARCHY_INSTALLER"; - isoImage.edition = lib.mkForce "minimal"; - - # Essential packages for installation - environment.systemPackages = with pkgs; [ - # Core utilities - git - vim - curl - wget - - # TUI installer dependencies - gum - - # Disk tools - inputs.disko.packages.${pkgs.stdenv.hostPlatform.system}.disko - parted - btrfs-progs - cryptsetup - - # Network tools - networkmanager - - # System info - lshw - pciutils - usbutils - - # Installer command - (pkgs.writeShellScriptBin "nomarchy-install" '' - exec /etc/install.sh "$@" - '') - ]; - - # Enable NetworkManager for easy network setup - networking.networkmanager.enable = true; - - # Auto-login to TTY as root for installation - services.getty.autologinUser = lib.mkForce "root"; - - # Display welcome message and installer info - environment.etc."motd".text = '' - - ╔══════════════════════════════════════════════════════════════╗ - ║ ║ - ║ NOMARCHY INSTALLER ║ - ║ ║ - ║ Run 'nomarchy-install' to start the installation wizard ║ - ║ ║ - ║ For network setup: nmtui ║ - ║ For manual install: see /etc/nomarchy/ ║ - ║ ║ - ╚══════════════════════════════════════════════════════════════╝ - - ''; - - # Make the installer script available - environment.etc."install.sh" = { - source = ../installer/install.sh; - mode = "0755"; - }; - - environment.etc."hardware-db.sh" = { - source = ../installer/hardware-db.sh; - mode = "0644"; - }; - - # Symlink for easy access (merged into systemPackages above) - # The nomarchy-install script is created by writeShellScriptBin in the main list - - # Include disko configuration - environment.etc."disko-config.nix".source = ../installer/disko-config.nix; - - # Include Nomarchy source for installation - environment.etc."nomarchy".source = inputs.self; - - # Disable graphical stuff - this is TTY only - services.xserver.enable = false; - services.displayManager.sddm.enable = lib.mkForce false; - - # Ensure we have a proper shell environment - programs.bash.completion.enable = true; - - # Auto-launch installer on the main TTY (tty1) - programs.bash.loginShellInit = '' - if [ "$(tty)" = "/dev/tty1" ]; then - nomarchy-install - fi - ''; - - # Include documentation - documentation.enable = true; - documentation.man.enable = true; -} diff --git a/hosts/nomarchy-live.nix b/hosts/nomarchy-live.nix deleted file mode 100644 index bbda20c..0000000 --- a/hosts/nomarchy-live.nix +++ /dev/null @@ -1,112 +0,0 @@ -{ config, pkgs, inputs, lib, ... }: - -{ - # Live-ISO defaults. The install.sh keymap step calls `hyprctl keyword - # input:kb_layout` at runtime so the user's pick takes effect during the - # install, and writes the chosen value into the *installed* system. - console.keyMap = lib.mkDefault "us"; - i18n.defaultLocale = lib.mkDefault "en_US.UTF-8"; - services.xserver.xkb.layout = lib.mkDefault "us"; - - # ISO Branding - isoImage.volumeID = lib.mkForce "NOMARCHY_LIVE"; - isoImage.edition = lib.mkForce "graphical"; - - # Home Manager activation for the nixos live user is provided by the - # home-manager.nixosModules.home-manager module wired up in flake.nix. - - environment.systemPackages = with pkgs; [ - git - gum - alacritty - parted - btrfs-progs - cryptsetup - mkpasswd - inputs.disko.packages.${pkgs.stdenv.hostPlatform.system}.disko - (pkgs.makeDesktopItem { - name = "install-nomarchy"; - desktopName = "Install Nomarchy"; - exec = "alacritty -e sudo /etc/install.sh"; - terminal = false; - categories = [ "System" ]; - }) - ]; - - # Ensure the live environment user has the necessary groups for graphical acceleration and audio - users.users.nixos.extraGroups = [ "wheel" "video" "render" "audio" "networkmanager" ]; - - # Graphics support for live environment. - # - # Force-loading every GPU driver in the initrd (amdgpu+radeon+nouveau+i915) - # panics most machines — only one of them can claim the GPU and the others - # explode. List them under `availableKernelModules` instead so udev only - # loads the one that matches the present hardware. We still force the AMD - # pair when Plymouth needs KMS early; `virtio_gpu` is auto-loaded when - # we're booted in QEMU via nomarchy-test-live-iso. - boot.initrd.availableKernelModules = [ - "amdgpu" "radeon" "nouveau" "i915" "virtio_gpu" - ]; - 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"; - }; - - environment.etc."hardware-db.sh" = { - source = ../installer/hardware-db.sh; - mode = "0644"; - }; - - environment.etc."disko-config.nix" = { - source = ../installer/disko-config.nix; - }; - - environment.etc."nomarchy".source = inputs.self; - - # Embed the git revision the ISO was built from so install.sh can pin the - # generated flake to the exact same commit. `inputs.self.rev` exists only - # when the flake is built from a clean git tree; from a dirty worktree we - # fall back to dirtyRev (which won't be resolvable by `git+https`, so the - # installer treats it as "unpinned"). Empty file = unpinned. - environment.etc."nomarchy-rev".text = - if inputs.self ? rev then inputs.self.rev - else ""; - - # Auto-login to the graphical session - services.displayManager.autoLogin.enable = true; - services.displayManager.autoLogin.user = "nixos"; - - # 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 '' - - Welcome to the Nomarchy live environment. - - To install Nomarchy: sudo /etc/install.sh - To preview the install: sudo /etc/install.sh --dry-run - To resume an interrupted run: sudo /etc/install.sh --resume - - The graphical session autologins as 'nixos' (no password). - The 'nixos' and 'root' accounts have empty passwords. - ''; -} diff --git a/installer/disko-config.nix b/installer/disko-config.nix deleted file mode 100644 index f74421d..0000000 --- a/installer/disko-config.nix +++ /dev/null @@ -1,136 +0,0 @@ -# Nomarchy Golden-Path Disk Configuration -# -# Single source of truth for the installer's disko layout. The single-disk -# and multi-disk paths differ only in (a) whether `extraDrives` is empty and -# (b) the BTRFS profile (multi adds `-d single -m raid1` plus the additional -# /dev/mapper/* devices). Pass arguments via: -# -# disko --argstr mainDrive /dev/nvme0n1 \ -# --arg extraDrives '[]' \ -# disko-config.nix -# -# disko --argstr mainDrive /dev/nvme0n1 \ -# --arg extraDrives '[ "/dev/sdb" "/dev/sdc" ]' \ -# disko-config.nix -# -# Replaces the previous sed-templated disko-golden.nix + disko-btrfs-multi.nix -# pair. Templating Nix via shell-escaped string substitution proved fragile -# (commit 3aadc36 fixed one escaping bug; another was waiting to happen) — -# function arguments are the right shape. -{ mainDrive -, extraDrives ? [] -, ... -}: - -let - hasExtras = extraDrives != []; - - # Sanitize a device path into something usable as both a disko attr name - # and a /dev/mapper/ name. /dev/sdb -> dev_sdb, /dev/nvme0n2 -> dev_nvme0n2. - sanitize = path: builtins.replaceStrings [ "/" "-" ] [ "_" "_" ] path; - - extraName = drive: "extra_" + sanitize drive; - extraLuks = drive: "crypted_" + sanitize drive; - - mkExtraDisk = drive: { - name = extraName drive; - value = { - type = "disk"; - device = drive; - content = { - type = "gpt"; - partitions.luks = { - size = "100%"; - content = { - type = "luks"; - name = extraLuks drive; - passwordFile = "/tmp/nomarchy-luks.key"; - settings.allowDiscards = true; - content.type = "btrfs"; - }; - }; - }; - }; - }; - - # BTRFS extraArgs: - # - single: just `-f` (force) — no need to enumerate devices, the FS is - # created on the one /dev/mapper/crypted device disko emits. - # - multi: `-f -d single -m raid1 <extra mappers...>` — data striped - # across devices for capacity, metadata mirrored for safety. - btrfsExtraArgs = - if hasExtras then - [ "-f" "-d" "single" "-m" "raid1" ] - ++ map (d: "/dev/mapper/" + extraLuks d) extraDrives - else - [ "-f" ]; - - # The main LUKS mapping name varies between layouts so the postCreateHook - # (which mounts the freshly created BTRFS to take the impermanence-rollback - # snapshot) targets the right /dev/mapper entry. - mainLuksName = if hasExtras then "crypted_main" else "crypted"; - - # Multi-device BTRFS on LUKS requires that we explicitly tell systemd-initrd - # to wait for ALL LUKS devices to be decrypted before attempting to mount - # the filesystem, otherwise it might try to mount as soon as the first one - # appears and then hang or fail. - allLuksNames = [ mainLuksName ] ++ map extraLuks extraDrives; - btrfsMountOptions = [ "compress=zstd" "noatime" "x-systemd.device-timeout=0" ] - ++ map (n: "x-systemd.requires=/dev/mapper/" + n) allLuksNames; - - rootBtrfs = { - type = "btrfs"; - extraArgs = btrfsExtraArgs; - subvolumes = { - "@" = { mountpoint = "/"; mountOptions = btrfsMountOptions; }; - "@persist" = { mountpoint = "/persist"; mountOptions = btrfsMountOptions; }; - "@home" = { mountpoint = "/home"; mountOptions = btrfsMountOptions; }; - "@nix" = { mountpoint = "/nix"; mountOptions = btrfsMountOptions; }; - "@log" = { mountpoint = "/var/log"; mountOptions = btrfsMountOptions; }; - "@snapshots" = { mountpoint = "/.snapshots"; mountOptions = btrfsMountOptions; }; - }; - postCreateHook = '' - MNTPOINT=$(mktemp -d) - mount -t btrfs /dev/mapper/${mainLuksName} $MNTPOINT - btrfs subvolume snapshot -r $MNTPOINT/@ $MNTPOINT/root-blank - umount $MNTPOINT - ''; - }; - -in { - disko.devices.disk = { - main = { - type = "disk"; - device = mainDrive; - content = { - type = "gpt"; - partitions = { - # 1 GiB ESP — fits several kernel generations + initrd + Plymouth. - ESP = { - priority = 1; - name = "ESP"; - start = "1M"; - end = "1G"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = [ "umask=0077" ]; - }; - }; - luks = { - size = "100%"; - content = { - type = "luks"; - name = mainLuksName; - passwordFile = "/tmp/nomarchy-luks.key"; - settings.allowDiscards = true; - content = rootBtrfs; - }; - }; - }; - }; - }; - } // builtins.listToAttrs (map mkExtraDisk extraDrives); -} diff --git a/installer/hardware-db.sh b/installer/hardware-db.sh deleted file mode 100644 index 0cceca4..0000000 --- a/installer/hardware-db.sh +++ /dev/null @@ -1,212 +0,0 @@ -# Nomarchy Hardware Database -# -# Flat table mapping detected DMI fields to `nixos-hardware` flake modules. -# Sourced by installer/install.sh at runtime. -# -# Format (pipe-separated): -# sys_vendor_regex | product_regex | nomarchy_hw_opt | nixos_hardware_module -# -# - Matching is bash `[[ =~ ]]` regex, case-insensitive. -# - Use "_" for `nomarchy_hw_opt` when there's no Nomarchy toggle to set. -# - Multiple modules can be joined with "+" if a device needs >1. -# - First match wins — put more specific entries (e.g. exact model years) above -# broader fallbacks. -# -# To add a new device: grab the string from `/sys/class/dmi/id/product_name` -# on the target machine and add a line here. See: -# https://github.com/NixOS/nixos-hardware/tree/master/<vendor> -# for the list of available module attributes. - -HARDWARE_DB=( - # Framework --------------------------------------------------------------- - # Module names follow nixos-hardware's actual attrs — for Framework 13 - # the per-generation modules dropped the "13-" prefix. - "Framework|Laptop 16.*AMD|isFramework=true|framework-16-7040-amd" - "Framework|Laptop 16.*Ryzen AI 300|isFramework=true|framework-16-amd-ai-300-series" - "Framework|Laptop 13.*Ryzen AI 300|isFramework=true|framework-amd-ai-300-series" - "Framework|Laptop 13.*Ryzen 7040|isFramework=true|framework-13-7040-amd" - "Framework|Laptop 13.*Core Ultra|isFramework=true|framework-intel-core-ultra-series1" - "Framework|Laptop 13.*13th Gen Intel|isFramework=true|framework-13th-gen-intel" - "Framework|Laptop 13.*12th Gen Intel|isFramework=true|framework-12th-gen-intel" - "Framework|Laptop 13.*11th Gen Intel|isFramework=true|framework-11th-gen-intel" - "Framework|Laptop \(13.*|isFramework=true|framework" - - # Dell XPS / Precision / Latitude ---------------------------------------- - "Dell|XPS 15 9500|isXPS=true|dell-xps-15-9500" - "Dell|XPS 15 9510|isXPS=true|dell-xps-15-9510" - "Dell|XPS 15 9520|isXPS=true|dell-xps-15-9520" - "Dell|XPS 13 9310|isXPS=true|dell-xps-13-9310" - "Dell|XPS 13 9370|isXPS=true|dell-xps-13-9370" - "Dell|XPS 13 9380|isXPS=true|dell-xps-13-9380" - "Dell|XPS 13 7390|isXPS=true|dell-xps-13-7390" - "Dell|Precision 5530|_|dell-precision-5530" - "Dell|Latitude 7490|_|dell-latitude-7490" - "Dell|Latitude 7430|_|dell-latitude-7430" - "Dell|Latitude 7420|_|dell-latitude-7420" - - # Lenovo ThinkPad -------------------------------------------------------- - # X1 Carbon: the per-gen modules are named "x1-Nth-gen", not - # "x1-carbon-genN" — both naming schemes appeared in nixos-hardware and - # the old DB references picked the wrong one. Carbon-specific quirks - # match the X1 modules because the X1 series IS the Carbon line. - "LENOVO|ThinkPad X1 Carbon Gen 11|_|lenovo-thinkpad-x1-11th-gen" - "LENOVO|ThinkPad X1 Carbon Gen 10|_|lenovo-thinkpad-x1-10th-gen" - "LENOVO|ThinkPad X1 Carbon Gen 9|_|lenovo-thinkpad-x1-9th-gen" - "LENOVO|ThinkPad X1 Carbon Gen 7|_|lenovo-thinkpad-x1-7th-gen" - "LENOVO|ThinkPad X1 Carbon Gen 6|_|lenovo-thinkpad-x1-6th-gen" - "LENOVO|ThinkPad X1 Extreme|_|lenovo-thinkpad-x1-extreme" - "LENOVO|ThinkPad X1 Nano|_|lenovo-thinkpad-x1-nano-gen1" - "LENOVO|ThinkPad T14 Gen 3|_|lenovo-thinkpad-t14-amd-gen3" - "LENOVO|ThinkPad T14 Gen 2|_|lenovo-thinkpad-t14-amd-gen2" - "LENOVO|ThinkPad T14 Gen 1|_|lenovo-thinkpad-t14-amd-gen1" - "LENOVO|ThinkPad T480|_|lenovo-thinkpad-t480" - "LENOVO|ThinkPad L13|_|lenovo-thinkpad-l13" - "LENOVO|ThinkPad P14s.*Gen 5|_|lenovo-thinkpad-p14s-amd-gen5" - "LENOVO|ThinkPad P14s.*Gen 4|_|lenovo-thinkpad-p14s-amd-gen4" - "LENOVO|ThinkPad P14s.*Gen 3|_|lenovo-thinkpad-p14s-amd-gen3" - - # Microsoft Surface ------------------------------------------------------ - # nixos-hardware ships per-chip modules, not per-revision: Intel Surface - # Pros (6 through 10) all use `microsoft-surface-pro-intel`; AMD Surface - # Laptops use `microsoft-surface-laptop-amd`. Surface Pro 9 has its own - # variant for the SQ3 / ARM model — kept separate. Surface Book and - # Intel-based Surface Laptops have no dedicated module; we fall back to - # the common detection (chassis + cpu + gpu). - "Microsoft|Surface Pro 10|_|microsoft-surface-pro-intel" - "Microsoft|Surface Pro 9|_|microsoft-surface-pro-9" - "Microsoft|Surface Pro 8|_|microsoft-surface-pro-intel" - "Microsoft|Surface Pro 7|_|microsoft-surface-pro-intel" - "Microsoft|Surface Pro 6|_|microsoft-surface-pro-intel" - "Microsoft|Surface Pro 3|_|microsoft-surface-pro-3" - "Microsoft|Surface Laptop.*Ryzen|_|microsoft-surface-laptop-amd" - "Microsoft|Surface Go|_|microsoft-surface-go" - - # ASUS ROG Ally / Zephyrus / Strix -------------------------------------- - # Ally is the handheld (Z1 Extreme / Z1). RC71L is the original Ally, - # RC72LA is the Ally X — both share the same nixos-hardware module - # currently. - "ASUS.*|RC71L|_|asus-ally-rc71l" - "ASUS.*|RC72LA|_|asus-ally-rc71l" - "ASUS.*|ROG Ally|_|asus-ally-rc71l" - "ASUS.*|ROG Zephyrus G14.*GA402X|_|asus-zephyrus-ga402x" - "ASUS.*|ROG Zephyrus G14.*GA402|_|asus-zephyrus-ga402" - "ASUS.*|ROG Zephyrus G14.*GA401|_|asus-zephyrus-ga401" - "ASUS.*|ROG Zephyrus G15|_|asus-zephyrus-ga503" - "ASUS.*|ROG Zephyrus.*GU603|_|asus-zephyrus-gu603h" - "ASUS.*|ROG Strix G513|_|asus-rog-strix-g513im" - "ASUS.*|ROG Strix G533|_|asus-rog-strix-g533zw" - - # Apple (T2 Intel; M-series falls back to asahi elsewhere) --------------- - "Apple.*|MacBookPro15|isT2Mac=true|apple-t2" - "Apple.*|MacBookPro16|isT2Mac=true|apple-t2" - "Apple.*|MacBookAir8|isT2Mac=true|apple-t2" - "Apple.*|MacBookAir9|isT2Mac=true|apple-t2" - "Apple.*|iMac19|isT2Mac=true|apple-t2" - "Apple.*|iMacPro1|isT2Mac=true|apple-t2" - - # System76 --------------------------------------------------------------- - "System76|Oryx Pro.*|_|system76" - "System76|Lemur Pro.*|_|system76" - "System76|Darter Pro.*|_|system76" - "System76|Galago Pro.*|_|system76" - "System76|Pangolin.*|_|system76" - - # Devices nixos-hardware doesn't cover (yet) ----------------------------- - # Listed here so future contributors know they're known-unsupported, not - # accidentally missing: - # - Valve Steam Deck (Galileo / Jupiter): try Jovian-NixOS as a flake - # input instead — separate ecosystem, not in nixos-hardware. - # - Snapdragon X laptops (Surface Pro 11, Lenovo Yoga Slim 7x, …): - # aarch64-only and the Nomarchy installer is x86_64 only. - # - Raspberry Pi (ARM): same — installer is x86_64 only. -) - -# ---------------------------------------------------------------------------- -# nomarchy_detect_hw -# -# Inspects DMI + lspci + /sys to figure out which nixos-hardware modules fit -# the running machine. Emits (on stdout) one line per result: -# -# MODULE <module-name> -# OPT <nomarchy_hw_opt> # zero or more, one per line -# DETAIL <human-readable hit> -# -# Return codes: 0 if at least one module was detected, 1 otherwise. -# ---------------------------------------------------------------------------- -nomarchy_detect_hw() { - local sys_vendor product_name board_name cpu_vendor - sys_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || echo "") - product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "") - board_name=$(cat /sys/class/dmi/id/board_name 2>/dev/null || echo "") - cpu_vendor=$(lscpu 2>/dev/null | awk -F: '/Vendor ID/{gsub(/ /,"",$2); print $2; exit}') - - local detected=0 - - # CPU -------------------------------------------------------------------- - case "$cpu_vendor" in - AuthenticAMD) echo "MODULE common-cpu-amd"; echo "DETAIL cpu: AMD"; detected=1 ;; - GenuineIntel) echo "MODULE common-cpu-intel"; echo "DETAIL cpu: Intel"; detected=1 ;; - esac - - # GPU (lspci may list multiple; handle all) ----------------------------- - if command -v lspci >/dev/null 2>&1; then - local gpu_line nvidia=0 amdgpu=0 intelgpu=0 - while IFS= read -r gpu_line; do - case "$gpu_line" in - *"[10de:"*|*"NVIDIA"*) nvidia=1 ;; - *"[1002:"*|*"AMD/ATI"*|*"Advanced Micro Devices"*) amdgpu=1 ;; - *"[8086:"*|*"Intel Corporation"*) intelgpu=1 ;; - esac - done < <(lspci -nn 2>/dev/null | grep -iE 'vga|3d|display') - - (( nvidia )) && { echo "MODULE common-gpu-nvidia"; echo "DETAIL gpu: NVIDIA"; detected=1; } - (( amdgpu )) && { echo "MODULE common-gpu-amd"; echo "DETAIL gpu: AMD"; detected=1; } - (( intelgpu )) && { echo "MODULE common-gpu-intel"; echo "DETAIL gpu: Intel"; detected=1; } - fi - - # Laptop / SSD ---------------------------------------------------------- - if compgen -G "/sys/class/power_supply/BAT*" >/dev/null; then - echo "MODULE common-pc-laptop" - echo "DETAIL chassis: laptop (battery present)" - detected=1 - else - echo "MODULE common-pc" - echo "DETAIL chassis: desktop" - detected=1 - fi - - # Model-specific match from the DB -------------------------------------- - local entry v_re p_re opt mod - for entry in "${HARDWARE_DB[@]}"; do - IFS='|' read -r v_re p_re opt mod <<< "$entry" - shopt -s nocasematch - if [[ "$sys_vendor" =~ $v_re ]] && [[ "$product_name" =~ $p_re ]]; then - shopt -u nocasematch - echo "MODULE $mod" - [[ "$opt" != "_" ]] && echo "OPT $opt" - echo "DETAIL model: $sys_vendor $product_name → $mod" - detected=1 - break - fi - shopt -u nocasematch - done - - return $(( 1 - detected )) -} - -# ---------------------------------------------------------------------------- -# nomarchy_hw_summary -# -# Pretty-prints the output of `nomarchy_detect_hw` for TUI consumption. -# Reads from stdin. -# ---------------------------------------------------------------------------- -nomarchy_hw_summary() { - local line key val - while IFS= read -r line; do - key="${line%% *}" - val="${line#* }" - case "$key" in - DETAIL) echo " → $val" ;; - esac - done -} diff --git a/installer/install.sh b/installer/install.sh deleted file mode 100755 index 7b05d92..0000000 --- a/installer/install.sh +++ /dev/null @@ -1,2076 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy TTY Installer -# Golden path: BTRFS + LUKS2 encryption -# -# This is a minimal, single-path installer designed for TTY-only environments. -# For a customized installation, manually set up your disk and use the generated -# flake configuration as a starting point. - -# Load the hardware-detection database — resolved relative to this script so it -# works whether we're invoked from /etc/install.sh on the live ISO or straight -# from a checkout. -_NOMARCHY_INSTALL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# shellcheck source=hardware-db.sh -source "$_NOMARCHY_INSTALL_DIR/hardware-db.sh" - -# Colors and styling -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' -NC='\033[0m' # No Color -BOLD='\033[1m' - -# Installer state -NOMARCHY_REPO="" -NOMARCHY_REV="" -HOSTNAME="" -TARGET_DRIVE="" -USERNAME="" -LUKS_PASSWORD="" -USER_PASSWORD="" -USER_PASSWORD_HASH="" -TIMEZONE="UTC" -KEYMAP_LAYOUT="" -KEYMAP_VARIANT="" -LOCALE="" -FORM_FACTOR="" -HARDWARE_MODULES="" -NOMARCHY_HW_OPTS="" -SELECTED_PROFILES="" -# "" = not yet answered; "true"/"false" set by configure_impermanence. -ENABLE_IMPERMANENCE="" - -# Curated short lists for the keymap/locale prompts. "Other…" drops the user -# into the full `localectl` list via gum filter. -COMMON_KEYMAPS=( - us de fr gb es it pt br se no fi dk nl pl ru ua ch jp kr cn ar -) -COMMON_LOCALES=( - en_US.UTF-8 en_GB.UTF-8 de_DE.UTF-8 fr_FR.UTF-8 es_ES.UTF-8 - it_IT.UTF-8 pt_BR.UTF-8 pt_PT.UTF-8 nl_NL.UTF-8 sv_SE.UTF-8 - ja_JP.UTF-8 zh_CN.UTF-8 ko_KR.UTF-8 -) - -# CLI flags -DRY_RUN="false" -RESUME="false" - -# Resumable answers — passwords are NEVER persisted; the user re-enters them. -STATE_FILE="/tmp/nomarchy-install.state.sh" - -# ============================================================================ -# CLI / PERSISTENCE -# ============================================================================ - -usage() { - cat <<USAGE -Usage: install.sh [--dry-run] [--resume] [-h|--help] - - --dry-run Generate the flake into a tmpdir and run \`nix flake check\`. - Doesn't touch the disk, doesn't run nixos-install. - --resume Reuse answers from a previous interrupted run - (saved at $STATE_FILE — passwords excluded). - NOTE: the live ISO uses tmpfs, so the state file is lost - on reboot. --resume only works within the same live-ISO - session as the original interrupted run. - -h, --help Print this message. -USAGE -} - -parse_args() { - while (( $# > 0 )); do - case "$1" in - --dry-run) DRY_RUN="true" ;; - --resume) RESUME="true" ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;; - esac - shift - done -} - -# Persist non-secret answers so an interrupted install can pick up where it -# left off. Uses `declare -p` so each line is a self-contained `declare --` -# statement that `source` re-establishes verbatim. -# -# USER_PASSWORD_HASH is intentionally NOT persisted, even though a SHA-512 -# crypt hash isn't reversible. The contract is: after --resume, the password -# prompt re-runs. configure_user's early-return guard at the top of the -# function checks `[[ -n "$USER_PASSWORD_HASH" ]]` for exactly this reason — -# if you ever change that guard to skip on USERNAME+HOSTNAME alone, --resume -# will silently install a system with an empty password hash and lock the -# user out. Keep the guard checking the hash. -save_state() { - # The leading timestamp lets --resume surface "how old is this state?" - # and is parsed back via NOMARCHY_INSTALL_STATE_SAVED_AT. - { - echo "NOMARCHY_INSTALL_STATE_SAVED_AT=\"$(date -Iseconds)\"" - declare -p \ - TARGET_DRIVE USERNAME HOSTNAME TIMEZONE \ - KEYMAP_LAYOUT KEYMAP_VARIANT LOCALE FORM_FACTOR \ - ENABLE_IMPERMANENCE HARDWARE_MODULES NOMARCHY_HW_OPTS \ - SELECTED_PROFILES NOMARCHY_REV - } > "$STATE_FILE" -} - -# Pretty-print "X minutes/hours/days ago" from an ISO-8601 timestamp. -# Falls back to the raw string if `date -d` can't parse it (defensive — -# the timestamp is always produced by `date -Iseconds` above, but we -# don't want a stale state file to crash --resume). -format_age() { - local saved="$1" saved_epoch now_epoch diff - saved_epoch=$(date -d "$saved" +%s 2>/dev/null) || { echo "$saved"; return; } - now_epoch=$(date +%s) - diff=$(( now_epoch - saved_epoch )) - if (( diff < 60 )); then echo "${diff}s ago" - elif (( diff < 3600 )); then echo "$((diff / 60))m ago" - elif (( diff < 86400 )); then echo "$((diff / 3600))h $((diff % 3600 / 60))m ago" - else echo "$((diff / 86400))d ago" - fi -} - -load_state() { - if [[ "$RESUME" != "true" ]]; then - return - fi - - # --resume with no state file is almost always operator error — the - # most common cause is "rebooted the live ISO and forgot tmpfs eats - # /tmp/". Fail loudly so the user doesn't sit through a fresh prompt - # cycle thinking it was resumed. - if [[ ! -f "$STATE_FILE" ]]; then - error "--resume was passed but no saved state exists at $STATE_FILE." - info "The live ISO uses tmpfs — saved state doesn't survive a reboot." - info "Re-run install.sh without --resume to start fresh." - exit 1 - fi - - # shellcheck disable=SC1090 - source "$STATE_FILE" - - # If the saved target drive isn't visible right now, every later - # disk-phase step will fail with cryptic errors. Catch it here. - # Live ISOs frequently get their non-boot USB sticks unplugged - # between sessions, and dev hosts sometimes have non-deterministic - # /dev/sdX numbering. - if [[ -n "${TARGET_DRIVE:-}" ]] && [[ ! -b "$TARGET_DRIVE" ]]; then - error "Saved target drive $TARGET_DRIVE is no longer a block device." - info "The drive may have been unplugged or renamed since the saved run." - info "Delete $STATE_FILE and re-run without --resume." - exit 1 - fi - - # Show what we're resuming into so the user can Ctrl-C if they're on - # the wrong host before any password / disk-wipe prompts fire. - local age="unknown age" - if [[ -n "${NOMARCHY_INSTALL_STATE_SAVED_AT:-}" ]]; then - age=$(format_age "$NOMARCHY_INSTALL_STATE_SAVED_AT") - fi - info "Resumed from $STATE_FILE (saved $age)" - if [[ -n "${USERNAME:-}" || -n "${HOSTNAME:-}" || -n "${TARGET_DRIVE:-}" ]]; then - info " Target: ${TARGET_DRIVE:-?} → ${USERNAME:-?} @ ${HOSTNAME:-?}" - fi -} - -# ============================================================================ -# UTILITY FUNCTIONS -# ============================================================================ - -# Helper to run commands via nix run -nrun() { - local cmd="$1" - shift - if command -v "$cmd" >/dev/null 2>&1; then - "$cmd" "$@" - else - nix run --extra-experimental-features "nix-command flakes" "nixpkgs#$cmd" -- "$@" - fi -} - -clear_step_state() { - case "$1" in - select_disk) TARGET_DRIVE="" ;; - get_luks_passphrase) LUKS_PASSWORD="" ;; - configure_user) USERNAME=""; HOSTNAME=""; USER_PASSWORD=""; USER_PASSWORD_HASH="" ;; - select_keymap_locale) KEYMAP_LAYOUT=""; KEYMAP_VARIANT=""; LOCALE="" ;; - select_timezone) TIMEZONE="" ;; - select_hardware) HARDWARE_MODULES=""; NOMARCHY_HW_OPTS="" ;; - confirm_form_factor) FORM_FACTOR="" ;; - configure_impermanence) ENABLE_IMPERMANENCE="" ;; - select_profiles) SELECTED_PROFILES="" ;; - select_nomarchy_rev) NOMARCHY_REV="" ;; - esac -} - -header() { - clear - nrun gum style \ - --foreground 212 --border-foreground 212 --border double \ - --align center --width 60 --margin "1 2" --padding "2 4" \ - "NOMARCHY INSTALLER" "Nomarchy Distribution" - echo "" -} - -section() { - echo "" - nrun gum style --foreground 14 --bold "━━━ $1 ━━━" - echo "" -} - -success() { - nrun gum style --foreground 10 "✓ $1" -} - -error() { - nrun gum style --foreground 9 "✗ $1" -} - -info() { - nrun gum style --foreground 12 "→ $1" -} - -# ============================================================================ -# STEP 1: ENVIRONMENT CHECK -# ============================================================================ - -check_environment() { - section "Environment Check" - - # Check for root - if [[ $EUID -ne 0 ]]; then - error "This installer must be run as root (use sudo)" - exit 1 - fi - success "Running as root" - - # Confirm we booted via UEFI. The disko config lays out a GPT + ESP and - # we install systemd-boot/grub-efi; on a BIOS-booted host that doesn't - # work and would partial-install before failing. - if [[ ! -d /sys/firmware/efi ]]; then - error "This installer requires a UEFI system (no /sys/firmware/efi)." - info "Reboot in UEFI mode (disable CSM/legacy in firmware setup) and try again." - exit 1 - fi - success "UEFI boot detected" - - # 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="$(realpath /etc/nomarchy)" - elif [[ -d "$(dirname "$0")/.." ]] && [[ -f "$(dirname "$0")/../flake.nix" ]]; then - NOMARCHY_REPO="$(realpath "$(dirname "$0")/..")" - fi - - if [[ -z "$NOMARCHY_REPO" ]]; then - error "Nomarchy repository not found" - exit 1 - fi - success "Found Nomarchy at $NOMARCHY_REPO" - - # Capture the exact commit we're installing from. The generated flake - # pins `nomarchy.url` to this revision so the installed system can't - # silently drift onto a newer (possibly breaking) main. - # - # Three sources, in priority order: - # 1. /etc/nomarchy-rev — written at ISO build time from `inputs.self.rev` - # (the only source that works on a normal live-ISO install, because - # `inputs.self` strips .git from the Nix store copy at /etc/nomarchy). - # 2. `git rev-parse HEAD` in the repo — works when running the installer - # from a dev checkout instead of the live ISO. - # 3. Empty → unpinned, user gets a loud confirmation prompt below. - if [[ -z "$NOMARCHY_REV" ]] && [[ -f /etc/nomarchy-rev ]]; then - NOMARCHY_REV=$(tr -d '[:space:]' < /etc/nomarchy-rev) - fi - if [[ -z "$NOMARCHY_REV" ]] && command -v git >/dev/null 2>&1 && [[ -d "$NOMARCHY_REPO/.git" ]]; then - NOMARCHY_REV=$(git -C "$NOMARCHY_REPO" rev-parse HEAD 2>/dev/null || echo "") - fi - if [[ -n "$NOMARCHY_REV" ]]; then - success "Pinning Nomarchy to $NOMARCHY_REV" - else - error "Could not determine Nomarchy revision." - info "The installed system would silently track upstream main, which" - info "defeats the point of locking inputs at install time." - if [[ "$DRY_RUN" != "true" ]]; then - if ! nrun gum confirm --default=false \ - "Continue anyway with an unpinned (tracking main) configuration?"; then - exit 1 - fi - fi - fi - - # Check internet - gum spin --spinner dot --title "Checking internet connection..." -- sleep 1 - while ! ping -c 1 -W 2 1.1.1.1 &>/dev/null; do - error "No internet connection" - local choice - choice=$(gum choose "Open Network Manager (nmtui)" "Retry" "Exit") || exit 1 - case "$choice" in - *nmtui*) nmtui ;; - *Exit*) exit 1 ;; - esac - done - success "Internet connection verified" -} - -# ============================================================================ -# STEP 2: DISK SELECTION -# ============================================================================ - -# Resolve the block device(s) backing the running live ISO so the disk -# picker can hide them. Picking the live USB by mistake destroys the -# installer's own boot media mid-run — always the worst-case outcome. -# We walk the live-ISO mountpoints (NixOS live ISO uses /iso for the -# squashfs source plus an overlay at /), resolve each to its parent -# disk via `lsblk -no PKNAME`, and emit a deduped list of /dev/<disk> -# entries on stdout. Nothing emitted = no live-ISO devices detected -# (e.g. running the installer from a regular shell during development). -detect_live_iso_devices() { - local seen=" " - local mp src parent - for mp in / /iso /run/initramfs/live /nix/.ro-store /nix/store; do - src=$(findmnt -no SOURCE "$mp" 2>/dev/null) || continue - [[ "$src" == /dev/* ]] || continue - parent=$(lsblk -no PKNAME "$src" 2>/dev/null | head -n1) - if [[ -n "$parent" ]]; then - parent="/dev/$parent" - else - parent="$src" - fi - case "$seen" in - *" $parent "*) ;; - *) seen+="$parent "; printf '%s\n' "$parent" ;; - esac - done -} - -# Minimum total capacity across all picked drives. 10 GiB is the smallest -# size where the install completes without immediate disk-pressure failures -# (1 GiB ESP + ~5 GiB nix closure + working set). -_MIN_INSTALL_BYTES=$((10 * 1024 * 1024 * 1024)) - -select_disk() { - section "Disk Selection" - - if [[ -n "$TARGET_DRIVE" ]]; then - success "Selected: $TARGET_DRIVE" - return 0 - fi - - # Build a richer drive table than the bare `NAME SIZE` lsblk default. - # Columns: NAME, SIZE, TYPE (NVMe/USB/SSD/HDD), VENDOR, MODEL, SERIAL. - # Empty fields render as "--" so column -t can still align them. - local raw rows="" - - # Filter out pseudo-devices and the live-ISO boot media. The boot-media - # filter is the important one: without it the user can pick the USB - # they booted from and the installer will format its own boot device - # mid-run. NOMARCHY_INSTALL_ALLOW_ISO_TARGET=1 disables this guard - # for the rare case someone genuinely wants to install onto the same - # device (e.g. a developer testing in a VM without a second disk). - local exclude_re='^(/dev/(loop|ram|zram|sr))' - local live_devices=() - if [[ "${NOMARCHY_INSTALL_ALLOW_ISO_TARGET:-0}" != "1" ]]; then - mapfile -t live_devices < <(detect_live_iso_devices) - local d - for d in "${live_devices[@]}"; do - [[ -n "$d" ]] || continue - # Anchor to end-of-line so /dev/sda doesn't also match /dev/sdaa. - exclude_re+="|^${d}$" - done - if (( ${#live_devices[@]} > 0 )); then - info "Excluding live-ISO device(s) from picker: ${live_devices[*]}" - fi - fi - - raw=$(lsblk -d -n -p -o NAME,SIZE,ROTA,TRAN,VENDOR,MODEL,SERIAL 2>/dev/null \ - | grep -vE "$exclude_re") - - while IFS= read -r line; do - if [[ -z "$line" ]]; then continue; fi - # NAME and SIZE are reliably whitespace-free; ROTA/TRAN are short - # tokens; VENDOR/MODEL/SERIAL can carry internal spaces. Pull the - # first four fields off the front, treat the rest as the - # vendor/model/serial trio split via the original lsblk column - # widths — easier to just re-query each device for clean values. - local dev size rota tran - read -r dev size rota tran _ <<<"$line" - - local type vendor model serial - case "$tran" in - nvme) type="NVMe" ;; - usb) type="USB" ;; - sata|scsi) - if [[ "$rota" == "1" ]]; then type="HDD"; else type="SSD"; fi - ;; - *) - if [[ "$rota" == "1" ]]; then type="HDD"; else type="SSD"; fi - ;; - esac - - vendor=$(lsblk -d -n -o VENDOR "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//') - model=$(lsblk -d -n -o MODEL "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//') - serial=$(lsblk -d -n -o SERIAL "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//') - if [[ -z "$vendor" ]]; then vendor="--"; fi - if [[ -z "$model" ]]; then model="--"; fi - if [[ -z "$serial" ]]; then serial="--"; fi - - # Tab-separated for column -t -s, then collapse internal whitespace - # in MODEL so multi-space brand strings don't break alignment. - rows+=$(printf '%s\t%s\t%s\t%s\t%s\t%s\n' \ - "$dev" "$size" "$type" "$vendor" "${model//$'\t'/ }" "$serial") - rows+=$'\n' - done <<<"$raw" - - if [[ -z "$rows" ]]; then - error "No installable drives found." - exit 1 - fi - - info "Available drives:" - echo "" - { - printf 'NAME\tSIZE\tTYPE\tVENDOR\tMODEL\tSERIAL\n' - printf '%s' "$rows" - } | column -t -s $'\t' - echo "" - - # gum choose gets the same aligned rows so the picker reads like the table. - local picker - picker=$(printf '%s' "$rows" | column -t -s $'\t') - local choice rc=0 - choice=$(printf '%s\n' "$picker" | nrun gum choose --no-limit --header "Select target drive(s) - Use Space to select multiple for BTRFS RAID/Single") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - TARGET_DRIVE=$(awk '{print $1}' <<<"$choice" | xargs) - - if [[ -z "$TARGET_DRIVE" ]]; then - error "No drive selected" - return 130 - fi - - if [[ "$DRY_RUN" != "true" ]]; then - # Total-capacity preflight. Disko fails late and obscurely on - # undersized media; surface it here while the picker is still open. - local total_bytes=0 sz d - for d in $TARGET_DRIVE; do - sz=$(lsblk -bdno SIZE "$d" 2>/dev/null) || sz=0 - total_bytes=$((total_bytes + sz)) - done - if (( total_bytes < _MIN_INSTALL_BYTES )); then - local human - human=$(numfmt --to=iec --suffix=B "$total_bytes" 2>/dev/null || echo "${total_bytes} B") - error "Total target capacity is $human; Nomarchy needs at least 10 GiB." - TARGET_DRIVE="" - return 130 - fi - - echo "" - nrun gum style --foreground 9 --bold "⚠ WARNING: All data on $TARGET_DRIVE will be DESTROYED!" - echo "" - rc=0 - nrun gum confirm "Are you sure you want to use $TARGET_DRIVE?" || rc=$? - if [[ $rc -ne 0 ]]; then - if [[ $rc -eq 130 || $rc -eq 1 ]]; then return 130; fi - error "Aborted" - exit 1 - fi - fi - - success "Selected: $TARGET_DRIVE" - save_state -} - -# ============================================================================ -# STEP 3: LUKS PASSPHRASE -# ============================================================================ - -get_luks_passphrase() { - if [[ "$DRY_RUN" == "true" ]]; then - info "Dry run: skipping LUKS passphrase prompt." - LUKS_PASSWORD="dryrun-not-used" - return - fi - - # Already set this session (review-edit loop iterated). Don't re-prompt; - # password isn't persisted across runs but is held in memory until - # execute_installation unsets it. - if [[ -n "${LUKS_PASSWORD:-}" ]]; then return; fi - - section "Disk Encryption" - - info "Your disk will be encrypted with LUKS2." - info "Enter a strong passphrase (you'll need this at every boot)." - echo "" - - local pass1 pass2 rc=0 - while true; do - rc=0 - pass1=$(nrun gum input --password --placeholder "Enter LUKS passphrase") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$pass1" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - - if [[ -z "$pass1" ]]; then continue; fi - - rc=0 - pass2=$(nrun gum input --password --placeholder "Confirm passphrase") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$pass2" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - - if [[ "$pass1" == "$pass2" ]]; then - LUKS_PASSWORD="$pass1" - break - else - error "Passphrases do not match. Try again." - fi - done - - success "Encryption passphrase set" -} - -# ============================================================================ -# STEP 4: USER CONFIGURATION -# ============================================================================ - -configure_user() { - section "User Configuration" - - if [[ -n "$USERNAME" && -n "$HOSTNAME" ]]; then - # Password check skipped in dry run or if already hashed in this session. - if [[ "$DRY_RUN" == "true" ]] || [[ -n "$USER_PASSWORD_HASH" ]]; then - success "User $USERNAME @ $HOSTNAME configured" - return 0 - fi - fi - - local rc=0 - if [[ -z "$USERNAME" ]]; then - rc=0 - USERNAME=$(nrun gum input --placeholder "Enter username (lowercase, no spaces)") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$USERNAME" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - - if [[ -z "$USERNAME" ]] || [[ ! "$USERNAME" =~ ^[a-z][a-z0-9_-]*$ ]]; then - # If they just hit Enter/Esc and we got here, they might want to go back - if [[ -z "$USERNAME" ]]; then return 130; fi - error "Invalid username" - exit 1 - fi - fi - success "Username: $USERNAME" - - if [[ -z "$HOSTNAME" ]]; then - info "Set a hostname for this machine" - rc=0 - HOSTNAME=$(nrun gum input --value "nomarchy" --placeholder "Hostname for this machine") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$HOSTNAME" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - - if [[ -z "$HOSTNAME" ]] || [[ ! "$HOSTNAME" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then - if [[ -z "$HOSTNAME" ]]; then return 130; fi - error "Invalid hostname (use lowercase letters, digits, and hyphens only)" - exit 1 - fi - fi - success "Hostname: $HOSTNAME" - - if [[ "$DRY_RUN" == "true" ]]; then - info "Dry run: skipping user password prompt." - USER_PASSWORD="dryrun-not-used" - # Stable placeholder hash so generated system.nix still parses as Nix. - # Never lands on a real install — dry-run skips nixos-install. - USER_PASSWORD_HASH='$6$dryrun$3xxK3aQ.0bGcv0fM2RhV4Q9oN3p1mYxz5kSjQ.bC8tZpZ7QnFv2cN0Yhd5lDqJ8X9mP2K1L0vR6BqWqzNk7Yo/' - save_state - return - fi - - # User password (can be same as LUKS or different) - info "Set a password for your user account" - local pass1 pass2 - while true; do - rc=0 - pass1=$(nrun gum input --password --placeholder "Enter user password") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$pass1" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - - if [[ -z "$pass1" ]]; then continue; fi - - rc=0 - pass2=$(nrun gum input --password --placeholder "Confirm user password") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$pass2" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - - if [[ "$pass1" == "$pass2" ]]; then - USER_PASSWORD="$pass1" - break - else - error "Passwords do not match. Try again." - fi - done - - # Hash now so we never have to embed the cleartext in /etc/nixos/system.nix - # (where it would be world-readable and break Nix parsing on quotes/backslash). - # mkpasswd is added to the live ISO via hosts/nomarchy-live.nix. - if ! command -v mkpasswd >/dev/null 2>&1; then - error "mkpasswd not found on the live ISO — cannot hash the user password." - exit 1 - fi - USER_PASSWORD_HASH=$(printf '%s' "$USER_PASSWORD" | mkpasswd -m sha-512 -s) - if [[ -z "$USER_PASSWORD_HASH" ]]; then - error "mkpasswd produced an empty hash." - exit 1 - fi - unset pass1 pass2 USER_PASSWORD - - success "User password set" - save_state -} - -# ============================================================================ -# STEP 5a: KEYBOARD & LANGUAGE -# ============================================================================ - -# Curated short list first ("Other…" drops into the full localectl list). -# Applied IMMEDIATELY to the running session so the rest of the install types -# correctly — TTY uses loadkeys, Hyprland uses hyprctl. Both best-effort. -select_keymap_locale() { - section "Keyboard & Language" - - if [[ -n "$KEYMAP_LAYOUT" && -n "$LOCALE" ]]; then - success "Keymap ($KEYMAP_LAYOUT) and Locale ($LOCALE) set" - return 0 - fi - - local choice rc=0 - if [[ -z "$KEYMAP_LAYOUT" ]]; then - rc=0 - choice=$(printf '%s\n' "${COMMON_KEYMAPS[@]}" "Other…" \ - | nrun gum choose --header "Keyboard layout") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - if [[ "$choice" == "Other…" ]]; then - rc=0 - KEYMAP_LAYOUT=$(localectl list-x11-keymap-layouts 2>/dev/null \ - | nrun gum filter --placeholder "Search keyboard layout…") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$KEYMAP_LAYOUT" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - else - KEYMAP_LAYOUT="$choice" - fi - if [[ -z "$KEYMAP_LAYOUT" ]]; then KEYMAP_LAYOUT="us"; fi - fi - success "Keyboard layout: $KEYMAP_LAYOUT" - - # Variant — optional. Only prompt if the layout actually has variants. - if [[ -z "$KEYMAP_VARIANT" ]]; then - local variants - variants=$(localectl list-x11-keymap-variants "$KEYMAP_LAYOUT" 2>/dev/null || true) - if [[ -n "$variants" ]]; then - local v - rc=0 - v=$(printf '(none)\n%s\n' "$variants" \ - | nrun gum filter --placeholder "Variant (optional)" --value "(none)") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$v" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - if [[ -n "$v" && "$v" != "(none)" ]]; then KEYMAP_VARIANT="$v"; fi - fi - fi - - if [[ -n "$KEYMAP_VARIANT" ]]; then - success "Variant: $KEYMAP_VARIANT" - else - success "Variant: (default)" - fi - - # Apply to the live session, best-effort. - loadkeys "$KEYMAP_LAYOUT" 2>/dev/null || true - if [[ -n "${WAYLAND_DISPLAY:-}" ]] && command -v hyprctl >/dev/null 2>&1; then - hyprctl keyword input:kb_layout "$KEYMAP_LAYOUT" >/dev/null 2>&1 || true - hyprctl keyword input:kb_variant "$KEYMAP_VARIANT" >/dev/null 2>&1 || true - fi - - if [[ -z "$LOCALE" ]]; then - local choice rc=0 - rc=0 - choice=$(printf '%s\n' "${COMMON_LOCALES[@]}" "Other…" \ - | nrun gum choose --header "Language / locale") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - if [[ "$choice" == "Other…" ]]; then - rc=0 - LOCALE=$(localectl list-locales 2>/dev/null \ - | nrun gum filter --placeholder "Search locale…") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$LOCALE" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - else - LOCALE="$choice" - fi - if [[ -z "$LOCALE" ]]; then LOCALE="en_US.UTF-8"; fi - fi - success "Locale: $LOCALE" - - save_state -} - -# ============================================================================ -# STEP 5: TIMEZONE -# ============================================================================ - -select_timezone() { - section "Timezone" - - if [[ -n "$TIMEZONE" ]]; then - success "Timezone set to $TIMEZONE" - return 0 - fi - - local timezones rc=0 - timezones=$(timedatectl list-timezones 2>/dev/null || echo "UTC") - rc=0 - TIMEZONE=$(echo "$timezones" | gum filter --placeholder "Search timezone...") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$TIMEZONE" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - if [[ -z "$TIMEZONE" ]]; then TIMEZONE="UTC"; fi - success "Timezone: $TIMEZONE" - save_state -} - -# ============================================================================ -# STEP 6: HARDWARE VENDOR -# ============================================================================ - -select_hardware() { - section "Hardware Configuration" - - if [[ -n "$HARDWARE_MODULES" ]]; then - success "Hardware configured" - return 0 - fi - - local dmi_vendor dmi_product detect_output - dmi_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || echo "Unknown") - dmi_product=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "Unknown") - info "DMI: $dmi_vendor / $dmi_product" - echo "" - - # Auto-detect CPU, GPU, chassis, and known model from hardware-db.sh. - detect_output=$(nomarchy_detect_hw || true) - - echo "Auto-detected:" - nomarchy_hw_summary <<< "$detect_output" - echo "" - - # Collect modules + nomarchy options from the detector output. - local modules=() hw_opts=() - while IFS= read -r line; do - case "$line" in - "MODULE "*) modules+=("${line#MODULE }") ;; - "OPT "*) hw_opts+=("${line#OPT }") ;; - esac - done <<< "$detect_output" - - # Let the user accept, extend, or replace the detection. - local choice rc=0 - while true; do - rc=0 - choice=$(nrun gum choose --header "Hardware configuration" \ - "Accept detected modules" \ - "Add an extra nixos-hardware module" \ - "Pick from the manual list (override)") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - - case "$choice" in - "Add an extra nixos-hardware module") - local extra extra_rc=0 - extra=$(nrun gum input --placeholder "e.g. asus-zephyrus-ga401 (no 'nixos-hardware.' prefix)") || extra_rc=$? - if [[ $extra_rc -eq 130 || $extra_rc -eq 1 || "$extra" == "not submitted" ]]; then continue; fi - if [[ $extra_rc -ne 0 ]]; then exit $extra_rc; fi - if [[ -n "$extra" ]]; then modules+=("$extra"); fi - break - ;; - "Pick from the manual list (override)") - modules=() - hw_opts=() - local manual_rc=0 - _select_hardware_manual modules hw_opts || manual_rc=$? - if [[ $manual_rc -eq 130 ]]; then continue; fi - if [[ $manual_rc -ne 0 ]]; then exit $manual_rc; fi - break - ;; - "Accept detected modules") - break - ;; - esac - done - - # De-duplicate while preserving order. - local seen="" uniq_mods=() m - for m in "${modules[@]}"; do - if [[ ":$seen:" != *":$m:"* ]]; then - uniq_mods+=("$m") - seen="$seen:$m" - fi - done - - # Emit a list the heredoc in generate_flake_config splats into - # hardware-selection.nix's imports. The heredoc already indents the first - # line by 4 spaces — we add real newlines + 4 spaces (via $'\n ') for - # subsequent lines so every entry lines up. - HARDWARE_MODULES="" - for m in "${uniq_mods[@]}"; do - [[ -z "$HARDWARE_MODULES" ]] || HARDWARE_MODULES+=$'\n ' - HARDWARE_MODULES+="inputs.nixos-hardware.nixosModules.${m}" - done - - # Same treatment for nomarchy.hardware.* toggles. - NOMARCHY_HW_OPTS="" - local o - for o in "${hw_opts[@]}"; do - # opt is e.g. `isFramework=true` → `nomarchy.hardware.isFramework = true;` - local key="${o%%=*}" val="${o#*=}" - NOMARCHY_HW_OPTS+="nomarchy.hardware.${key} = ${val};"$'\n ' - done - - success "Hardware configuration set (${#uniq_mods[@]} module$([[ ${#uniq_mods[@]} -eq 1 ]] || echo s))" - save_state -} - -# Manual fallback menu, kept for odd hardware the DB doesn't recognise yet. -# Writes into the two arrays named by its arguments (bash 4.3+ nameref). -_select_hardware_manual() { - local -n _mods_ref="$1" - local -n _opts_ref="$2" - - local vendor rc=0 - rc=0 - vendor=$(nrun gum choose --header "Pick vendor" \ - "Framework" "Dell" "Lenovo" "Apple (T2 Mac)" "Microsoft Surface" "ASUS" "System76" "Other...") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$vendor" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - case "$vendor" in - Framework) - local model model_rc=0 - model=$(nrun gum choose \ - "framework-16-7040-amd" \ - "framework-13-amd-ai-300-series" \ - "framework-13-7040-amd" \ - "framework-13-13th-gen-intel" \ - "framework-13-12th-gen-intel" \ - "framework-13-11th-gen-intel") || model_rc=$? - if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi - if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi - _mods_ref+=("$model") - _opts_ref+=("isFramework=true") - ;; - Dell) - local model model_rc=0 - model=$(nrun gum choose \ - "dell-xps-15-9500" "dell-xps-15-9510" "dell-xps-15-9520" \ - "dell-xps-13-9310" "dell-xps-13-9370" "dell-xps-13-9380" \ - "dell-precision-5530" "dell-latitude-7480") || model_rc=$? - if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi - if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi - _mods_ref+=("$model") - if [[ "$model" == *xps* ]]; then _opts_ref+=("isXPS=true"); fi - ;; - Lenovo) - local model model_rc=0 - model=$(nrun gum choose \ - "lenovo-thinkpad-x1-carbon-gen11" "lenovo-thinkpad-x1-carbon-gen10" \ - "lenovo-thinkpad-x1-carbon-gen9" "lenovo-thinkpad-x1-extreme" \ - "lenovo-thinkpad-t14-amd-gen3" "lenovo-thinkpad-t14-amd-gen2" \ - "lenovo-thinkpad-t480" "lenovo-thinkpad-l13") || model_rc=$? - if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi - if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi - _mods_ref+=("$model") - ;; - "Microsoft Surface") - local model model_rc=0 - model=$(nrun gum choose \ - "microsoft-surface-pro-9" "microsoft-surface-pro-8" "microsoft-surface-pro-7" \ - "microsoft-surface-laptop-5" "microsoft-surface-laptop-4" "microsoft-surface-laptop-3") || model_rc=$? - if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi - if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi - _mods_ref+=("$model") - ;; - ASUS) - local model model_rc=0 - model=$(nrun gum choose \ - "asus-zephyrus-ga403" "asus-zephyrus-ga402" "asus-zephyrus-ga401" \ - "asus-zephyrus-ga503" "asus-rog-strix-g513" "asus-zenbook-ux") || model_rc=$? - if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi - if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi - _mods_ref+=("$model") - ;; - System76) - _mods_ref+=("system76") - ;; - "Other...") - local custom custom_rc=0 - custom=$(nrun gum input --placeholder "e.g. asus-zephyrus-ga401 (no 'nixos-hardware.' prefix)") || custom_rc=$? - if [[ $custom_rc -eq 130 || $custom_rc -eq 1 || "$custom" == "not submitted" ]]; then return 130; fi - if [[ $custom_rc -ne 0 ]]; then exit $custom_rc; fi - if [[ -n "$custom" ]]; then _mods_ref+=("$custom"); fi - ;; - esac -} - -# ============================================================================ -# STEP 6b: FORM FACTOR (LAPTOP / DESKTOP) -# ============================================================================ - -# Auto-detects via /sys/class/power_supply/BAT* — same signal hardware-db.sh -# uses to pick common-pc-laptop vs common-pc. The user can flip the answer -# (e.g. a desktop with a UPS that exposes a BAT*, or a laptop that doesn't). -confirm_form_factor() { - section "Form Factor" - - if [[ -n "$FORM_FACTOR" ]]; then - success "Resumed: $FORM_FACTOR" - return - fi - - local default="desktop" - if compgen -G "/sys/class/power_supply/BAT*" >/dev/null; then - default="laptop" - fi - info "Auto-detected: $default" - - local rc=0 - nrun gum confirm "Treat this machine as a $default?" || rc=$? - if [[ $rc -eq 0 ]]; then - FORM_FACTOR="$default" - else - if [[ $rc -eq 130 ]]; then return 130; fi - FORM_FACTOR=$([[ "$default" == "laptop" ]] && echo desktop || echo laptop) - fi - success "Form factor: $FORM_FACTOR" - save_state -} - -# ============================================================================ -# STEP 7: IMPERMANENCE (OPTIONAL) -# ============================================================================ - -configure_impermanence() { - section "Impermanence (Optional)" - - if [[ -n "$ENABLE_IMPERMANENCE" ]]; then - if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then - success "Impermanence enabled" - else - info "Impermanence disabled" - fi - return 0 - fi - - info "Impermanence erases your root filesystem on every boot." - info "Only explicitly persisted files survive reboots." - info "This provides a clean, reproducible system." - echo "" - - local rc=0 - nrun gum confirm "Enable Impermanence?" || rc=$? - case "$rc" in - 0) - ENABLE_IMPERMANENCE="true" - success "Impermanence enabled" - ;; - 1) - ENABLE_IMPERMANENCE="false" - info "Impermanence disabled (traditional persistent root)" - ;; - 130) - return 130 - ;; - *) - ENABLE_IMPERMANENCE="false" - info "Impermanence disabled (traditional persistent root)" - ;; - esac - save_state - return 0 -} - -# ============================================================================ -# STEP 8: SOFTWARE PROFILES (OPTIONAL) -# ============================================================================ - -select_profiles() { - section "Software Profiles" - - if [[ -n "$SELECTED_PROFILES" ]]; then - success "Software profiles selected" - return 0 - fi - - info "Pick optional software profiles to include in your configuration." - info "Multiple selection allowed (Space to select, Enter to confirm)." - echo "" - - rc=0 - SELECTED_PROFILES=$(nrun gum choose --no-limit --header "Select Profiles" \ - "Dev (VSCode, Git, Lazygit, Zed, Docker)" \ - "Gaming (Steam, Gamemode, Lutris, Heroic)" \ - "Office (LibreOffice, Thunderbird, Obsidian, Zotero)" \ - "Media (VLC, OBS Studio, GIMP, Inkscape, Spotify)" \ - "CLI Utils (ripgrep, fd, bat, eza, zoxide, fzf)") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$SELECTED_PROFILES" == "not submitted" || "$action" == "not submitted" || "$choices" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - if [[ -z "$SELECTED_PROFILES" ]]; then - info "No profiles selected (minimal install)" - else - # Count selected profiles by number of lines - local count - count=$(echo "$SELECTED_PROFILES" | grep -c "^" || echo 0) - success "Selected $count profile(s)" - fi - save_state -} - -# ============================================================================ -# STEP 9: REVIEW & CONFIRM -# ============================================================================ - -review_configuration() { - section "Review Configuration" - - echo " Drive: $TARGET_DRIVE (BTRFS + LUKS2)" - echo " Hostname: $HOSTNAME" - echo " Username: $USERNAME" - echo " Keymap: $KEYMAP_LAYOUT${KEYMAP_VARIANT:+ ($KEYMAP_VARIANT)}" - echo " Locale: $LOCALE" - echo " Timezone: $TIMEZONE" - echo " Form factor: $FORM_FACTOR" - echo " Impermanence: $ENABLE_IMPERMANENCE" - echo " Profiles: ${SELECTED_PROFILES:-None}" - echo " Nomarchy rev: ${NOMARCHY_REV:-main (unpinned)}" - echo "" - - if [[ "$DRY_RUN" == "true" ]]; then - info "Dry run: skipping destructive confirmation." - return 0 - fi - - nrun gum style --foreground 9 "This will DESTROY all data on $TARGET_DRIVE" - echo "" - - local action rc=0 - rc=0 - action=$(nrun gum choose --header "Choose:" \ - "Continue with installation" \ - "Edit a field" \ - "Abort") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$action" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - case "$action" in - "Continue with installation") return 0 ;; - "Edit a field") edit_fields; return 2 ;; - *) error "Aborted"; exit 1 ;; - esac -} - -# Multi-select clear: each cleared field is re-prompted on the next loop -# iteration in main() because every prompt short-circuits when its var is -# non-empty. Hardware clears the cached `nixos-hardware` modules and per-host -# option lines together so they stay consistent. Passwords are never offered -# here — get_luks_passphrase short-circuits when LUKS_PASSWORD is already set, -# so editing a field doesn't re-ask for the LUKS passphrase. -edit_fields() { - section "Edit Fields" - - local choices rc=0 - rc=0 - choices=$(nrun gum choose --no-limit --header "Pick fields to re-enter (space to select, enter to confirm):" \ - "Drive ($TARGET_DRIVE)" \ - "User and host ($USERNAME @ $HOSTNAME)" \ - "Keymap and locale ($KEYMAP_LAYOUT / $LOCALE)" \ - "Timezone ($TIMEZONE)" \ - "Hardware ($HARDWARE_MODULES)" \ - "Form factor ($FORM_FACTOR)" \ - "Impermanence ($ENABLE_IMPERMANENCE)" \ - "Profiles (${SELECTED_PROFILES:-none})" \ - "Nomarchy rev (${NOMARCHY_REV:-main})") || rc=$? - if [[ $rc -eq 130 || $rc -eq 1 || "$choices" == "not submitted" ]]; then return 130; fi - if [[ $rc -ne 0 ]]; then exit $rc; fi - if [[ -z "$choices" ]]; then return 0; fi - - while IFS= read -r f; do - case "$f" in - "Drive"*) TARGET_DRIVE="" ;; - "User and host"*) USERNAME=""; HOSTNAME="" ;; - "Keymap"*) KEYMAP_LAYOUT=""; KEYMAP_VARIANT=""; LOCALE="" ;; - "Timezone"*) TIMEZONE="" ;; - "Hardware"*) HARDWARE_MODULES=""; NOMARCHY_HW_OPTS="" ;; - "Form factor"*) FORM_FACTOR="" ;; - "Impermanence"*) ENABLE_IMPERMANENCE="" ;; - "Profiles"*) SELECTED_PROFILES="" ;; - "Nomarchy rev"*) NOMARCHY_REV="" ;; - esac - done <<<"$choices" -} - -# ============================================================================ -# STEP 9: EXECUTION -# ============================================================================ - -# Pre-wipe the target drive before invoking disko. -# -# disko (at our pinned revision) gates two destructive steps on blkid: -# - lib/types/gpt.nix runs `sgdisk --clear` only when blkid sees no PT -# - lib/types/filesystem.nix skips mkfs entirely when blkid reports the -# target FS type already exists on the partition device -# -# On a previously-installed disk those branches mis-fire: blkid sees the old -# GPT and the old vfat ESP, so disko overlays its new partition entries on -# the existing table without zapping and skips mkfs.vfat, leaving the kernel -# to read a stale FAT BPB on the new (slightly different) ESP extent. mount -# then errors with "wrong fs type, bad option, bad superblock". -prewipe_target_drive() { - local drive="$1" - - info "Pre-wiping $drive (clearing stale signatures)..." - - # Tear down anything a prior aborted run left active. Order matters: - # mount holders -> swap -> LUKS mappings -> wipe. - umount -R /mnt 2>/dev/null || true - swapoff -a 2>/dev/null || true - - # Enumerate every active dm-crypt mapping and close those whose backing - # device is on this drive. The previous version only knew about the - # hardcoded names "crypted" and "crypted_main"; an aborted multi-disk - # run, a manual experiment, or a non-Nomarchy install would leave a - # mapping with a different name holding the device busy and silently - # break the wipe. - if command -v dmsetup >/dev/null 2>&1; then - local name backing - while read -r name _; do - [[ -n "$name" && "$name" != "No" ]] || continue # "No devices found" - backing=$(cryptsetup status "$name" 2>/dev/null \ - | awk '/^[[:space:]]*device:/ { print $2; exit }') || continue - [[ -n "$backing" ]] || continue - if [[ "$backing" == "$drive" || "$backing" == "${drive}"* ]]; then - info " Closing stale LUKS mapping: $name (backed by $backing)" - cryptsetup close "$name" - fi - done < <(dmsetup ls --target crypt 2>/dev/null) - fi - - # Wipe partition signatures. No `|| true` — the LUKS/swap teardown - # above should have released every holder; if wipefs still fails the - # device is genuinely busy and we want to surface that, not silently - # paper over it and let disko fail later with a confusing blkid error. - local part - if compgen -G "${drive}?*" >/dev/null; then - for part in "${drive}"?*; do - [[ -b "$part" ]] || continue - wipefs -af "$part" >/dev/null - done - fi - wipefs -af "$drive" >/dev/null - sgdisk --zap-all "$drive" >/dev/null - - # 16 MiB covers LUKS2 binary headers (0–4 MiB) and the BTRFS first - # superblock (64 KiB) — wipefs alone misses damaged variants of these. - dd if=/dev/zero of="$drive" bs=1M count=16 conv=fsync status=none - - partprobe "$drive" 2>/dev/null || true - # Bound the settle so a flapping USB device can't hang the installer. - udevadm settle --timeout=30 || info "udevadm settle timed out; continuing." - - # Sanity check: nothing should still be mounted off this drive after - # the wipe. If something is, refuse to hand the disk to disko. - if lsblk -no MOUNTPOINTS "$drive" 2>/dev/null | grep -qE '\S'; then - error "Drive $drive still has active mountpoints after pre-wipe." - error "Investigate with: lsblk $drive ; mount | grep $drive" - return 1 - fi - - success "Pre-wipe complete" -} - -_LUKS_KEY_PATH="/tmp/nomarchy-luks.key" - -# Wrap the disko invocation so a failure surfaces the last few lines of -# output and offers Retry / View full log / Abort. set -e is suspended for -# the disko call so we can inspect its exit code; restored on every path. -# -# By default disko's chatty output is hidden behind a `gum spin` spinner; -# the full log is captured and shown on failure or via `gum pager`. Set -# NOMARCHY_VERBOSE_DISKO=1 to stream disko output live instead. -run_disko_with_retry() { - local main_drive="$1" - local extras_nix="$2" - local disko_file="$NOMARCHY_REPO/installer/disko-config.nix" - local log - log=$(mktemp --suffix=.disko.log) - - while true; do - local rc=0 - if [[ "$NOMARCHY_VERBOSE_DISKO" == "1" ]]; then - set +e - disko --mode destroy,format,mount \ - --yes-wipe-all-disks \ - --argstr mainDrive "$main_drive" \ - --arg extraDrives "$extras_nix" \ - "$disko_file" 2>&1 | tee "$log" - rc=${PIPESTATUS[0]} - set -e - else - # Silent path: run disko in the background, write all output to - # $log, and show a spinner. Use a status file for the exit code - # because `gum spin` doesn't propagate the wrapped command's rc. - local status_file - status_file=$(mktemp --suffix=.disko.rc) - set +e - nrun gum spin --spinner dot --title "Partitioning disk(s) with disko..." -- \ - bash -c ' - disko --mode destroy,format,mount \ - --yes-wipe-all-disks \ - --argstr mainDrive "$1" \ - --arg extraDrives "$2" \ - "$3" > "$4" 2>&1 - echo $? > "$5" - ' _ "$main_drive" "$extras_nix" "$disko_file" "$log" "$status_file" - set -e - rc=$(cat "$status_file" 2>/dev/null || echo 1) - rm -f "$status_file" - fi - - if [[ $rc -eq 0 ]]; then - rm -f "$log" - return 0 - fi - - error "disko failed (exit $rc). Last lines of output:" - tail -n 30 "$log" | nrun gum style --foreground 9 --border normal --padding "0 1" - - local choice - choice=$(printf 'Retry\nView full log\nAbort\n' \ - | nrun gum choose --header "Disk partitioning failed. What now?") - case "$choice" in - Retry) - info "Re-running pre-wipe and retrying disko..." - local d - for d in $TARGET_DRIVE; do prewipe_target_drive "$d"; done - ;; - "View full log") - nrun gum pager < "$log" || less -RFX "$log" || cat "$log" - ;; - *) - rm -f "$log" - return $rc - ;; - esac - done -} - -execute_installation() { - if [[ "$DRY_RUN" == "true" ]]; then - execute_dry_run - return - fi - - section "Installing Nomarchy" - - # 9.1 Partition with disko - info "Partitioning disk(s)..." - for d in $TARGET_DRIVE; do - prewipe_target_drive "$d" - done - - # Build the extraDrives Nix-list literal for disko-config.nix. Empty - # list = single-disk path. The list is well-formed by construction - # here (each element is a /dev/* path the user already picked) so - # there's no escaping concern — unlike the previous sed-templated Nix. - local drives=($TARGET_DRIVE) - local main_drive="${drives[0]}" - local extras_nix="[]" - if (( ${#drives[@]} > 1 )); then - extras_nix="[" - local i - for (( i=1; i<${#drives[@]}; i++ )); do - extras_nix+=" \"${drives[$i]}\"" - done - extras_nix+=" ]" - fi - - # Provide the LUKS passphrase via tmpfs so the secret never touches a - # spinning disk. /dev/shm is tmpfs on the live ISO. The EXIT trap - # below guarantees the file is removed even if the script aborts - # between writing the key and the unset below. - install -m 600 /dev/null "$_LUKS_KEY_PATH" - trap 'rm -f "$_LUKS_KEY_PATH" 2>/dev/null || true' EXIT - printf '%s' "$LUKS_PASSWORD" > "$_LUKS_KEY_PATH" - - run_disko_with_retry "$main_drive" "$extras_nix" || exit 1 - - rm -f "$_LUKS_KEY_PATH" - unset LUKS_PASSWORD - success "Disk partitioned" - - # 9.2 Generate hardware config - info "Generating hardware configuration..." - mkdir -p /mnt/etc/nixos - nixos-generate-config --root /mnt - success "Hardware configuration generated" - - # 9.3 Generate flake configuration - info "Creating system configuration..." - generate_flake_config - success "Configuration generated" - - # 9.4 Resolve inputs once, here, and lock them. First boot then consumes - # the same flake.lock and doesn't re-resolve a newer upstream. - info "Resolving flake inputs (this pins nomarchy, nixpkgs, etc.)..." - ( - cd /mnt/etc/nixos - nix --extra-experimental-features "nix-command flakes" flake lock >/dev/null - ) - success "flake.lock written" - - # 9.5 Initialize git repo so `nix` treats /etc/nixos as a flake worktree. - info "Initializing git repository..." - ( - cd /mnt/etc/nixos - nrun git init -q - nrun git add . - nrun git config user.name "Nomarchy Installer" - nrun git config user.email "installer@nomarchy" - nrun git commit -qm "Initial Nomarchy configuration" - ) - success "Git repository initialized" - - # 9.6 Handle impermanence - if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then - info "Setting up impermanence..." - mkdir -p /mnt/persist/etc - mv /mnt/etc/nixos /mnt/persist/etc/ - mkdir -p /mnt/etc - ln -s ../persist/etc/nixos /mnt/etc/nixos - success "Impermanence configured" - fi - - # 9.7 Install the Nomarchy system from the freshly-generated flake. - info "Running nixos-install (this will take a while)..." - nixos-install --flake "/mnt/etc/nixos#$HOSTNAME" --no-root-passwd - success "Nomarchy installed" - - # 9.8 (Removed) Standalone Home Manager activation in a chroot. - # HM is now wired as a NixOS module in the generated flake, so - # `nixos-install` above already activated the user's dotfiles as part - # of system activation. No chroot dance required. - - # 9.9 Pre-flight: catch evaluation errors in the freshly-installed - # configuration *now*, while we can still fix them with `vi`, instead of - # at the user's first post-reboot rebuild. - info "Verifying configuration evaluates (nixos-rebuild dry-build)..." - if nixos-enter --root /mnt -- bash -c " - nixos-rebuild dry-build --flake /etc/nixos#$HOSTNAME 2>&1 | tail -20 - "; then - success "Configuration evaluates cleanly" - else - error "Pre-flight rebuild check failed." - info "The system is installed; fix /etc/nixos before rebooting if possible." - fi - - # 9.10 Set ownership of /etc/nixos to the main user - info "Setting ownership of /etc/nixos for $USERNAME..." - nixos-enter --root /mnt -- chown -R "$USERNAME:users" /etc/nixos - success "Ownership updated" - - success "Installation complete!" - rm -f "$STATE_FILE" -} - -# ---------------------------------------------------------------------------- -# Dry run: generate the flake into a tmpdir and parse-check it. Doesn't -# touch the disk; useful while iterating on the installer or to validate a -# saved state file before actually committing to the install. -# ---------------------------------------------------------------------------- -execute_dry_run() { - section "Dry Run" - - local tmp - tmp=$(mktemp -d -t nomarchy-dryrun.XXXXXX) - info "Generating configuration in $tmp" - - # Mock /mnt so the existing generator writes into the tmpdir. - local fake_root="$tmp/mnt" - mkdir -p "$fake_root/etc/nixos" - ln -snf "$fake_root" "$tmp/.mntlink" - # generate_flake_config writes to /mnt/etc/nixos directly. We can't - # easily re-target without a refactor — bind-mount instead so the - # absolute paths in the function still resolve to our tmpdir. - mount --bind "$fake_root" /mnt 2>/dev/null || true - - if [[ ! -d /mnt/etc/nixos ]]; then - mkdir -p /mnt/etc/nixos - fi - - # Stub hardware-configuration.nix — `nixos-generate-config` requires - # actually-mounted target filesystems, so we provide a syntactically - # valid placeholder for parse-checking only. - cat > /mnt/etc/nixos/hardware-configuration.nix <<'EOF' -{ ... }: { boot.loader.systemd-boot.enable = true; fileSystems."/" = { device = "/dev/null"; fsType = "tmpfs"; }; } -EOF - - generate_flake_config - - info "Running \`nix-instantiate --parse\` on each generated file..." - local f rc=0 - for f in flake.nix hardware-selection.nix system.nix home.nix; do - if nix-instantiate --parse "/mnt/etc/nixos/$f" >/dev/null 2>&1; then - success " $f" - else - error " $f failed to parse" - rc=1 - fi - done - - info "Generated files:" - ls -1 /mnt/etc/nixos/ - - umount /mnt 2>/dev/null || true - info "Output kept at $fake_root for inspection." - - if (( rc != 0 )); then - error "Dry run reported parse errors." - exit "$rc" - fi - success "Dry run OK — no disk touched." -} - -# ============================================================================ -# GENERATE FLAKE CONFIGURATION -# ============================================================================ - -generate_flake_config() { - # Impermanence must mount the same /dev/mapper name that disko created. - # disko-config.nix uses "crypted" for single-disk and "crypted_main" once - # extraDrives is non-empty — keep these in sync. - local impermanence_opt="" - if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then - local _main_luks_name="crypted" - local _drives=($TARGET_DRIVE) - if (( ${#_drives[@]} > 1 )); then - _main_luks_name="crypted_main" - fi - impermanence_opt=$'nomarchy.system.impermanence.enable = true;\n nomarchy.system.impermanence.mainLuksName = "'"$_main_luks_name"$'";\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="" - - while IFS= read -r profile; do - if [[ -z "$profile" ]]; then continue; fi - case "$profile" in - "Dev "*) - PROFILE_SYSTEM_OPTS+=$'\n # Dev profile\n nomarchy.system.virtualization.docker.enable = true;' - PROFILE_HOME_PACKAGES+=$'\n vscode\n zed-editor\n lazygit\n gh\n docker-compose' - ;; - "Gaming "*) - PROFILE_SYSTEM_OPTS+=$'\n # Gaming profile\n programs.steam.enable = true;\n programs.gamemode.enable = true;' - PROFILE_HOME_PACKAGES+=$'\n steam\n lutris\n heroic' - ;; - "Office "*) - PROFILE_HOME_PACKAGES+=$'\n libreoffice\n thunderbird\n obsidian\n zotero' - ;; - "Media "*) - PROFILE_HOME_PACKAGES+=$'\n vlc\n obs-studio\n gimp\n inkscape\n spotify' - ;; - "CLI Utils "*) - PROFILE_HOME_PACKAGES+=$'\n ripgrep\n fd\n bat\n eza\n zoxide\n fzf' - ;; - esac - done <<< "$SELECTED_PROFILES" - - # Pin the upstream Nomarchy flake to the exact commit we're installing - # from so the first post-reboot `nixos-rebuild` doesn't silently pull a - # newer main. Fall back to tracking main if we couldn't resolve a SHA. - # Upstream lives on the self-hosted Gitea at git.bemagri.xyz; flakes - # consume it via the `git+https://` URL form. - local nomarchy_url - if [[ -n "$NOMARCHY_REV" ]]; then - nomarchy_url="git+https://git.bemagri.xyz/bernardo/Nomarchy.git?rev=$NOMARCHY_REV" - else - nomarchy_url="git+https://git.bemagri.xyz/bernardo/Nomarchy.git" - fi - - # flake.nix — the generator uses a non-quoted heredoc so $HOSTNAME, - # $USERNAME, and $nomarchy_url expand inline. - cat > /mnt/etc/nixos/flake.nix <<FLAKE_EOF -{ - description = "My Nomarchy Configuration"; - - inputs = { - nomarchy.url = "$nomarchy_url"; - }; - - # Two-track Nomarchy workflow: - # * System + dotfiles (full) → sudo nixos-rebuild switch --flake /etc/nixos#$HOSTNAME - # * Dotfiles only (fast iter) → nomarchy-env-update (standalone home-manager switch) - # - # Home Manager is wired both ways: - # - As a NixOS module under \`nixosConfigurations.$HOSTNAME\` so first boot - # after install already has dotfiles in place and every nixos-rebuild - # reconciles them. This is what makes the install actually usable. - # - As a standalone \`homeConfigurations.$USERNAME\` so theme switches and - # dotfile iteration don't require a full system rebuild. - outputs = { self, nomarchy, ... }@inputs: - let - inherit (nomarchy.inputs) nixpkgs home-manager; - system = "x86_64-linux"; - pkgs = import nixpkgs { - inherit system; - overlays = [ nomarchy.overlays.default ]; - config.allowUnfree = true; - }; - in - { - nixosConfigurations.$HOSTNAME = nixpkgs.lib.nixosSystem { - specialArgs = { inputs = nomarchy.inputs; }; - modules = [ - { - nixpkgs.hostPlatform = system; - nixpkgs.overlays = [ nomarchy.overlays.default ]; - } - ./hardware-configuration.nix - ./hardware-selection.nix - nomarchy.nixosModules.system - ./system.nix - - home-manager.nixosModules.home-manager - { - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - home-manager.backupFileExtension = "hm-bak"; - home-manager.extraSpecialArgs = { inputs = nomarchy.inputs; }; - home-manager.users.$USERNAME = { - imports = [ nomarchy.nixosModules.home ./home.nix ]; - home.stateVersion = "25.11"; - }; - } - ]; - }; - - # Standalone Home Manager — \`home-manager switch --flake /etc/nixos#$USERNAME\` - # (which is what \`nomarchy-env-update\` runs). Kept alongside the NixOS - # module above so dotfile/theme iterations can skip a full system rebuild. - homeConfigurations.$USERNAME = home-manager.lib.homeManagerConfiguration { - inherit pkgs; - extraSpecialArgs = { inputs = nomarchy.inputs; }; - modules = [ - nomarchy.nixosModules.home - ./home.nix - { - home.username = "$USERNAME"; - home.homeDirectory = "/home/$USERNAME"; - home.stateVersion = "25.11"; - } - ]; - }; - }; -} -FLAKE_EOF - - # hardware-selection.nix - cat > /mnt/etc/nixos/hardware-selection.nix << EOF -{ inputs, ... }: -{ - imports = [ - $HARDWARE_MODULES - ]; - $NOMARCHY_HW_OPTS$EXTRA_LUKS_OPTS -} -EOF - - # system.nix — curated system-level options. Uncomment what you want and - # run \`sudo nixos-rebuild switch --flake /etc/nixos#$HOSTNAME\` to apply. - # XKB variant is optional — only emit when the user picked one. - local xkb_variant_line="" - if [[ -n "$KEYMAP_VARIANT" ]]; then - xkb_variant_line=" variant = \"$KEYMAP_VARIANT\";" - fi - - cat > /mnt/etc/nixos/system.nix << EOF -{ pkgs, ... }: -{ - networking.hostName = "$HOSTNAME"; - time.timeZone = "$TIMEZONE"; - - # UEFI bootloader. Disko lays out a 1 GiB ESP at /boot — switch to - # boot.loader.grub if you're installing on a legacy-BIOS machine. - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - # Keyboard & language — set by the installer. - console.keyMap = "$KEYMAP_LAYOUT"; - i18n.defaultLocale = "$LOCALE"; - services.xserver.xkb = { - layout = "$KEYMAP_LAYOUT"; -$xkb_variant_line - }; - - # Physical form factor — gates UI affordances (battery widget, etc). - nomarchy.system.formFactor = "$FORM_FACTOR"; - - $impermanence_opt - $PROFILE_SYSTEM_OPTS - - # Compressed RAM swap. Near-free memory headroom on small machines and - # harmless on big ones — kernel only uses it under pressure. Disable if - # you've enabled disk swap or hibernation. - zramSwap.enable = true; - - # System-wide packages. Most tools belong in home.nix instead — only put - # things here that need to be available to all users or to root (e.g. CLI - # tools used by sudo, system admin utilities). - environment.systemPackages = with pkgs; [ - home-manager - # --- CLI tools useful as root --- - # wget - # curl - # rsync - # htop - # tree - # tmux - ]; - - services.displayManager.autoLogin.enable = true; - services.displayManager.autoLogin.user = "$USERNAME"; - - users.users."$USERNAME" = { - isNormalUser = true; - # SHA-512 crypt hash generated by mkpasswd during install. Cleartext - # never touches /etc/nixos. Change later with \`passwd\`. - initialHashedPassword = "$USER_PASSWORD_HASH"; - extraGroups = [ "networkmanager" "wheel" "video" "audio" "render" ]; - }; - - # --- Optional system services --- - # Uncomment to enable. Some require extra groups on your user (see below). - - # Containers / virtualization - # virtualisation.docker.enable = true; # adds "docker" group - # virtualisation.libvirtd.enable = true; # adds "libvirtd" group — needed for virt-manager - - # Networking / sync - # services.tailscale.enable = true; - # services.syncthing = { - # enable = true; - # user = "$USERNAME"; - # dataDir = "/home/$USERNAME"; - # }; - - # Printing - # services.printing.enable = true; - - # Flatpak (alternative app delivery) - # services.flatpak.enable = true; - # xdg.portal.enable = true; - - # Gaming (system-level — pairs with home.packages.steam) - # programs.steam.enable = true; - # programs.gamemode.enable = true; - - # --- Optional Nomarchy modules --- - # Each line is a one-shot toggle for a Tier 1 system feature. - - # BTRFS timeline snapshots of /. Auto-skips on non-BTRFS. - # nomarchy.system.snapper.enable = true; - - # Suspend-then-hibernate on lid/idle/power-key. Requires disk swap. - # nomarchy.system.hibernation.enable = true; - # nomarchy.system.hibernation.idleMinutes = 30; - - # Rootless Podman with \`docker\` compatibility. - # nomarchy.system.containers.enable = true; - - # libvirt + virt-manager + OVMF. - # nomarchy.system.virtualization.libvirt.enable = true; - - # GNOME Keyring auto-unlock + gcr SSH agent (default: on). - # nomarchy.system.keyring.enable = false; - - # fcitx5 input method (CJK / IME). - # nomarchy.system.inputMethod.enable = true; - - system.stateVersion = "25.11"; -} -EOF - - # home.nix — curated app menu. Uncomment what you want and run - # `nomarchy-env-update` to apply. - # - # NB: not heredoc-quoted — we expand $FORM_FACTOR. Any literal `$` or - # backtick in the body must be escaped. - 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"; - - # Keyboard layout for Hyprland's native Wayland session. system.nix - # writes services.xserver.xkb.layout (XWayland) and console.keyMap - # (TTY) to the same value, but Hyprland reads its own input config so - # this option needs to be set independently. - nomarchy.keymap = { - layout = "$KEYMAP_LAYOUT"; - variant = "$KEYMAP_VARIANT"; - }; - - # User-level packages (Home Manager). - # - # Nomarchy already ships the core Hyprland ecosystem (mako, hyprlock, swww, - # wl-clipboard, grim, slurp, rofi, etc.). The list below includes the - # default user apps and a menu of extras — uncomment what you want and - # run \`nomarchy-env-update\`. - home.packages = with pkgs; [ - # --- Enabled by default --- - firefox - xfce.thunar - imv # Image viewer - mpv # Video player - fastfetch # System info at login - chromium # Secondary browser - $PROFILE_HOME_PACKAGES - - # --- Editors & dev --- - # vscode - # jetbrains.idea-community - # neovide - # zed-editor - # lazygit - # gh # GitHub CLI - # docker-compose - # postman - # dbeaver-bin - - # --- Productivity --- - # obsidian - # libreoffice - # thunderbird - # zathura # PDF viewer - # zotero - # xournalpp - - # --- Media --- - # vlc - # obs-studio - # gimp - # inkscape - # kdenlive - # spotify - # audacity - # yt-dlp - - # --- Comms --- - # discord - # telegram-desktop - # signal-desktop - # slack - # zoom-us - - # --- Security --- - # keepassxc - # bitwarden-desktop - # _1password-gui - - # --- Gaming --- - # steam - # lutris - # heroic - - # --- CLI / utilities --- - # ripgrep - # fd - # bat - # eza - # zoxide - # fzf - # httpie - # tldr - ]; - - # --- Nomarchy app modules --- - - # These enable Nomarchy's curated configurations for the respective apps. - # Most are enabled by default to give you a complete desktop experience. - # Enabling the module also installs the corresponding package. - nomarchy.apps = { - alacritty.enable = true; - btop.enable = true; - elephant.enable = true; - swayosd.enable = true; - walker.enable = true; - vscode.enable = true; - kitty.enable = true; - lazygit.enable = true; - tmux.enable = true; - - # opencode AI coding CLI integration (deploys ~/.config/opencode/opencode.json). - # The \`opencode\` package itself is not installed automatically — add it to - # \`home.packages\` above if you want it on PATH. - # opencode.enable = true; - - # Deploys themed config if you install the package (Ghostty is not in nixpkgs) - # ghostty.enable = true; - }; - - # --- Optional Nomarchy app modules --- - - # Extra Home Manager modules go here (program configs, services, etc.). -} -EOF - - # state.json — consumed by core/system/state.nix at every nixos-rebuild - # and mutated by toggle scripts (nomarchy-tz-select, nomarchy-setup-fido2, - # nomarchy-toggle-hybrid-gpu, nomarchy-wifi-powersave, ...). Those scripts - # `jq` the file in place and fail hard if it doesn't exist or isn't - # valid JSON, so a fresh install MUST ship one. - # - # Source of truth: lib/state-schema.nix. We eval its `system` block and - # overlay the installer-chosen timezone, so this generator no longer - # drifts from the schema or the *.nix consumers — the previous heredoc - # was the last source-of-truth split after the state centralization. - # Adding a new default in the schema now reaches the installer without - # any further plumbing. - local _state_tz="${TIMEZONE:-UTC}" - nix --extra-experimental-features 'nix-command flakes' eval \ - --impure --raw \ - --expr " - let - flake = builtins.getFlake \"$NOMARCHY_REPO\"; - lib = flake.inputs.nixpkgs.lib; - schema = import \"$NOMARCHY_REPO/lib/state-schema.nix\" { inherit lib; }; - state = schema.system // { timezone = \"$_state_tz\"; }; - in builtins.toJSON state - " | nrun jq '.' > /mnt/etc/nixos/state.json - - # 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 -} - -# ============================================================================ -# FINISH -# ============================================================================ - -finish() { - header - - nrun gum style --foreground 10 --bold --align center "INSTALLATION COMPLETE!" - echo "" - echo "Nomarchy has been successfully installed." - echo "" - echo "Next steps:" - echo " 1. Remove the installation media" - echo " 2. Reboot your computer" - echo " 3. Log in as: $USERNAME (host: $HOSTNAME)" - echo " 4. Your configuration lives at /etc/nixos/" - echo "" - echo "Rebuild commands:" - echo " • System + dotfiles: sudo nixos-rebuild switch --flake /etc/nixos#$HOSTNAME" - echo " • Dotfiles only: nomarchy-env-update (fast standalone home-manager switch)" - echo "" - echo "Tip: run 'nomarchy-themes-prebuild' once to pre-cache every theme" - echo " variant. Theme switches after that are instant (no rebuild)." - echo "" - - # Unmount /mnt before either reboot or returning to the live shell: - # - Reboot: clean unmount avoids dirty BTRFS, which would otherwise - # force a longer first-boot fsck/replay. - # - Decline: leaving /mnt mounted blocks a second `install.sh` run on - # the same live ISO (disko refuses to wipe a busy device). - # `-R` recursively unmounts /mnt/boot, /mnt/home, /mnt/nix, etc.; the - # `|| true` absorbs the case where /mnt was already torn down. - if nrun gum confirm "Reboot now?"; then - umount -R /mnt 2>/dev/null || true - reboot - else - umount -R /mnt 2>/dev/null || true - fi -} - -# ============================================================================ -# MAIN -# ============================================================================ - -main() { - parse_args "$@" - header - load_state - - check_environment - - local steps=( - select_disk - get_luks_passphrase - configure_user - select_keymap_locale - select_timezone - select_hardware - confirm_form_factor - configure_impermanence - select_profiles - ) - - local current_step=0 - while true; do - if (( current_step < 0 )); then - error "Installation aborted by user." - exit 1 - fi - - if (( current_step < ${#steps[@]} )); then - local step_func="${steps[current_step]}" - - # Run the step. Each step returns 130 if the user presses Esc. - local rc=0 - "$step_func" || rc=$? - - if (( rc != 0 )); then - if (( rc == 130 )); then - # Clear the current step so it re-prompts if we return to it - clear_step_state "$step_func" - - # Flush terminal input buffer to prevent one Esc from cascading - # through multiple consecutive prompts. - sleep 0.1 - read -t 0.1 -n 10000 discard 2>/dev/null || true - - # Go back one step - current_step=$(( current_step - 1 )) - - # If we aren't at the very beginning, clear the TARGET - # step's variables too, so the user is forced to re-enter. - if (( current_step >= 0 )); then - clear_step_state "${steps[current_step]}" - fi - continue - fi - exit "$rc" - fi - current_step=$(( current_step + 1 )) - else - # All steps done, review the configuration. - local review_rc=0 - review_configuration || review_rc=$? - - if [[ "$review_rc" == "0" ]]; then - break - elif [[ "$review_rc" == "2" ]]; then - # User chose "Edit a field". Restart from step 0; functions - # with still-set variables will return 0 and skip ahead. - current_step=0 - elif [[ "$review_rc" == "130" ]]; then - # Go back to the last step. - current_step=$(( ${#steps[@]} - 1 )) - clear_step_state "${steps[current_step]}" - continue - else - exit "$review_rc" - fi - fi - done - - execute_installation - - # Skip the reboot prompt on a dry run — nothing to reboot into. - if [[ "$DRY_RUN" == "true" ]]; then - info "Dry run finished." - exit 0 - fi - - finish -} - -main "$@" diff --git a/lib.nix b/lib.nix new file mode 100644 index 0000000..674ad95 --- /dev/null +++ b/lib.nix @@ -0,0 +1,93 @@ +# Downstream sugar: one call wires a whole machine flake. +# Users own ONLY system.nix, home.nix and theme-state.json; their flake.nix +# is generated once (by `nix flake init -t` or the future installer) and +# never hand-edited. The raw exports (nixosModules/homeModules/overlays) +# in flake.nix remain the escape hatch for power users. +{ nixpkgs, home-manager, nixos-hardware, nomarchy }: + +{ + mkFlake = + { src # the downstream flake directory (./.) + , username # login name; also the homeConfigurations attr + , hardwareProfile ? null # nixos-hardware module name(s): string, list, or null + , system ? "x86_64-linux" + }: + let + inherit (nixpkgs) lib; + + # One profile or several (the installer's autodetection emits a few + # common-* modules alongside the model-specific one). + profileNames = + if hardwareProfile == null then [ ] + else if builtins.isList hardwareProfile then hardwareProfile + else [ hardwareProfile ]; + + lookupProfile = name: + if builtins.hasAttr name nixos-hardware.nixosModules then + nixos-hardware.nixosModules.${name} + else + throw '' + nomarchy.lib.mkFlake: unknown hardwareProfile "${name}". + ${let + close = builtins.filter + (candidate: lib.strings.levenshteinAtMost 3 candidate name) + (builtins.attrNames nixos-hardware.nixosModules); + in + lib.optionalString (close != [ ]) + "Did you mean: ${lib.concatStringsSep ", " close}?\n" + }Valid names are the attribute names of nixos-hardware.nixosModules: + https://github.com/NixOS/nixos-hardware + or list them locally with: + nix eval github:NixOS/nixos-hardware#nixosModules --apply builtins.attrNames + Omit hardwareProfile entirely if your machine has no profile. + ''; + + hardwareModules = map lookupProfile profileNames; + + pkgs = import nixpkgs { + inherit system; + overlays = [ nomarchy.overlays.default ]; + }; + in + { + # System layer — rebuilt rarely: + # sudo nixos-rebuild switch --flake .#default + nixosConfigurations.default = nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { inherit username; }; + modules = + [ + nomarchy.nixosModules.nomarchy + # The standalone HM CLI ships with the system so theme switching + # (`nomarchy-theme-sync apply` → `home-manager switch`) works + # out of the box — same pinned input as the desktop modules. + { environment.systemPackages = [ home-manager.packages.${system}.home-manager ]; } + ] + ++ hardwareModules + ++ [ + (src + "/hardware-configuration.nix") # from nixos-generate-config + (src + "/system.nix") + ]; + }; + + # Desktop layer — every theme change, no sudo: + # home-manager switch --flake .#<username> + # (`nomarchy-theme-sync apply` runs this for you.) + homeConfigurations.${username} = home-manager.lib.homeManagerConfiguration { + inherit pkgs; + modules = [ + nomarchy.homeModules.nomarchy + (src + "/home.nix") + { + # Written by nomarchy-theme-sync; reading it is pure — the + # file is part of the downstream flake's source. + nomarchy.stateFile = src + "/theme-state.json"; + home = { + inherit username; + homeDirectory = "/home/${username}"; + }; + } + ]; + }; + }; +} diff --git a/lib/default.nix b/lib/default.nix deleted file mode 100644 index a90b941..0000000 --- a/lib/default.nix +++ /dev/null @@ -1,110 +0,0 @@ -# Nomarchy Shared Library -# Centralized utilities to reduce code duplication across modules -{ lib }: - -let - # Import theme palettes once - used by multiple modules - palettes = import ./../themes/palettes; - - # Unified state reading function - # Handles both JSON and plain text files with graceful fallbacks - readState = { file, default }: - if builtins.pathExists file then - let - content = builtins.readFile file; - cleanContent = lib.removeSuffix "\n" content; - in - if lib.hasSuffix ".json" (toString file) then - builtins.fromJSON cleanContent - else - cleanContent - else - default; - - # Read home state from unified location - readHomeState = homeDirectory: - readState { - file = "${homeDirectory}/.config/nomarchy/state.json"; - default = {}; - }; - - # Read system state - readSystemState = - readState { - file = "/etc/nixos/state.json"; - default = {}; - }; - - # Resolve wallpaper path with fallback - # Returns either the user-specified wallpaper or a default from the theme - resolveWallpaper = { wallpaperPath, themeName, assetsPath }: - if wallpaperPath != "" then - wallpaperPath - else - let - themeBackgrounds = assetsPath + "/${themeName}/backgrounds"; - defaultBackground = assetsPath + "/catppuccin/backgrounds/1-totoro.png"; - in - if builtins.pathExists themeBackgrounds then - let - backgrounds = builtins.attrNames (builtins.readDir themeBackgrounds); - in - if backgrounds != [] then - "${themeBackgrounds}/${builtins.head (builtins.sort (a: b: a < b) backgrounds)}" - else - defaultBackground - else - defaultBackground; - - # Get current palette from theme name - getPalette = themeName: - (palettes.${themeName} or palettes.nord).palette; - - # Get full color scheme from theme name - getColorScheme = themeName: - palettes.${themeName} or palettes.nord; - - # Check if a theme is light mode - isThemeLightMode = { themeName, assetsPath }: - builtins.pathExists (assetsPath + "/${themeName}/light.mode"); - - # Get icons theme for a given theme - getIconsTheme = { themeName, assetsPath, default ? "Yaru-blue" }: - let - iconsFile = assetsPath + "/${themeName}/icons.theme"; - in - if builtins.pathExists iconsFile then - lib.removeSuffix "\n" (builtins.readFile iconsFile) - else - default; - - # Map an icon-theme name (as declared in a palette's icons.theme file) to - # the nixpkgs attribute that ships it. Anything unknown falls back to - # yaru-theme so GTK still has a working default. - # - # When adding a palette that uses a new icon family, extend this mapping - # so `nomarchy-theme-set <name>` auto-installs the matching package. - iconThemePackage = { iconsTheme, pkgs }: - if lib.hasPrefix "Yaru" iconsTheme then pkgs.yaru-theme - else if lib.hasPrefix "Everforest" iconsTheme then pkgs.everforest-gtk-theme - else if lib.hasPrefix "Papirus" iconsTheme then pkgs.papirus-icon-theme - else if lib.hasPrefix "Adwaita" iconsTheme then pkgs.adwaita-icon-theme - else if lib.hasPrefix "breeze" iconsTheme || lib.hasPrefix "Breeze" iconsTheme then pkgs.kdePackages.breeze-icons - else pkgs.yaru-theme; - - # Get list of available theme names - themeNames = builtins.attrNames palettes; - -in { - inherit - palettes - readHomeState - readSystemState - resolveWallpaper - getPalette - getColorScheme - isThemeLightMode - getIconsTheme - iconThemePackage - themeNames; -} diff --git a/lib/state-schema.nix b/lib/state-schema.nix deleted file mode 100644 index 888cc58..0000000 --- a/lib/state-schema.nix +++ /dev/null @@ -1,63 +0,0 @@ -# Nomarchy State Schema -# -# Defines the default values for every state.json field that's consumed by a -# Nix option. Read by core/{system,home}/options.nix (for `default = …`) and -# by core/{system,home}/state.nix (for `or` fallbacks). -# -# state.json may also hold runtime-only fields that aren't declared here — -# notably `welcome_done`, managed by `nomarchy-welcome`. Those are intentionally -# off-schema because no Nix option reads them; the schema is the "consumed by -# Nix" surface, not the full state.json shape. -{ lib }: - -{ - # Home state defaults (user preferences) - home = { - # Theme and appearance - theme = "summer-night"; - wallpaper = ""; - font = "JetBrainsMono Nerd Font"; - panelPosition = "top"; - nightlightTemperature = 4000; - - # Feature toggles - suspend = true; - screensaver = true; - idle = true; - nightlight = false; - waybar = true; - - hyprland = { - gaps_in = 5; - gaps_out = 10; - border_size = 2; - scale = "auto"; # Default monitor scale. Options: "auto", "1", "1.25", "1.5", "2" - }; - }; - - # System state defaults (system-level configuration) - system = { - # Theme (can differ from home for system-level theming) - theme = "summer-night"; - - # Timezone - timezone = "UTC"; - - # DNS configuration - dns = "DHCP"; # Options: "DHCP", "Cloudflare", "Google", "Custom" - customDns = []; - - # Wi-Fi settings - wifi = { - powersave = true; - }; - - # Optional features - features = { - fingerprint = false; - fido2 = false; - hybridGPU = false; - }; - }; - -} diff --git a/modules/home/btop.nix b/modules/home/btop.nix new file mode 100644 index 0000000..7dfd4bd --- /dev/null +++ b/modules/home/btop.nix @@ -0,0 +1,61 @@ +# btop — themed from per-theme assets, baked at eval time. +# A hand-made themes/<slug>/btop.theme asset wins; otherwise a theme is +# generated from the palette, so custom themes get a themed btop too. +{ config, lib, ... }: + +let + t = config.nomarchy.theme; + c = t.colors; + + asset = config.nomarchy.themesDir + "/${t.slug}/btop.theme"; + + generated = + let + pairs = { + main_bg = c.base; + main_fg = c.text; + title = c.text; + hi_fg = c.accent; + selected_bg = c.overlay; + selected_fg = c.text; + inactive_fg = c.muted; + graph_text = c.subtext; + meter_bg = c.surface; + proc_misc = c.accentAlt; + cpu_box = c.accent; + mem_box = c.good; + net_box = c.accentAlt; + proc_box = c.surface; + div_line = c.overlay; + temp_start = c.good; + temp_mid = c.warn; + temp_end = c.bad; + cpu_start = c.good; + cpu_mid = c.warn; + cpu_end = c.bad; + free_start = c.good; + used_start = c.warn; + download_start = c.accent; + upload_start = c.accentAlt; + }; + in + lib.concatStringsSep "\n" + (lib.mapAttrsToList (k: v: "theme[${k}]=\"${v}\"") pairs) + "\n"; +in +{ + config = lib.mkIf config.nomarchy.btop.enable { + programs.btop = { + enable = true; + settings = { + color_theme = "nomarchy"; + theme_background = false; # let the terminal's opacity show through + vim_keys = true; + }; + }; + + xdg.configFile."btop/themes/nomarchy.theme" = + if builtins.pathExists asset + then { source = asset; } + else { text = generated; }; + }; +} diff --git a/modules/home/default.nix b/modules/home/default.nix new file mode 100644 index 0000000..699ce68 --- /dev/null +++ b/modules/home/default.nix @@ -0,0 +1,36 @@ +# Nomarchy — Home Manager entry point. +# Consume this via homeModules.nomarchy (flake.nix), which also pulls in +# the stylix home module that stylix.nix configures. +{ config, pkgs, ... }: + +{ + imports = [ + ./options.nix # the nomarchy.* option surface + ./theme.nix # ingests theme-state.json, owns the live-sync hooks + ./stylix.nix # GTK/Qt/cursor/fonts from the same JSON + ./hyprland.nix + ./waybar.nix + ./ghostty.nix + ./btop.nix + ]; + + home.stateVersion = "26.05"; + + home.packages = with pkgs; [ + awww # wallpaper daemon with animated transitions (the swww fork) + fuzzel # launcher / dmenu (used by the theme picker bind) + libnotify + ]; + + home.sessionVariables = { + TERMINAL = config.nomarchy.terminal; + NIXOS_OZONE_WL = "1"; # Electron/Chromium native Wayland + + # Where the Nomarchy flake (and therefore theme-state.json) lives on + # disk. nomarchy-theme-sync writes its state here; rebuilds read from + # here. Clone/symlink your flake to this path. + NOMARCHY_PATH = "$HOME/.nomarchy"; + }; + + programs.home-manager.enable = true; +} diff --git a/modules/home/ghostty.nix b/modules/home/ghostty.nix new file mode 100644 index 0000000..01c45f5 --- /dev/null +++ b/modules/home/ghostty.nix @@ -0,0 +1,41 @@ +# Ghostty — Nomarchy's default terminal, themed from theme-state.json. +# Colors, fonts and the full 16-color ANSI palette are baked from the +# JSON at eval time. +{ config, lib, ... }: + +let + t = config.nomarchy.theme; + c = t.colors; +in +{ + programs.ghostty = lib.mkIf config.nomarchy.ghostty.enable { + enable = true; + enableBashIntegration = true; + + settings = { + # ── Typography (from theme-state.json) ──────────────────────── + font-family = t.fonts.mono; + font-size = t.fonts.size; + + # ── Colors (from theme-state.json) ──────────────────────────── + background = c.base; + foreground = c.text; + cursor-color = c.accent; + selection-background = c.overlay; + selection-foreground = c.text; + split-divider-color = c.surface; + + # "N=#rrggbb" entries; Ghostty accepts repeated `palette` keys, + # which the HM module renders from this list. + palette = lib.imap0 (i: color: "${toString i}=${color}") t.ansi; + + # ── Chrome ──────────────────────────────────────────────────── + background-opacity = t.ui.terminalOpacity; + window-padding-x = 12; + window-padding-y = 12; + window-decoration = false; + gtk-single-instance = true; + confirm-close-surface = false; + }; + }; +} diff --git a/modules/home/hyprland.nix b/modules/home/hyprland.nix new file mode 100644 index 0000000..6a44b8a --- /dev/null +++ b/modules/home/hyprland.nix @@ -0,0 +1,146 @@ +# Hyprland — fully driven by config.nomarchy.theme (theme-state.json). +# Gaps, borders and colors are baked from the JSON at eval time; theme +# changes arrive via `home-manager switch`. +{ config, lib, ... }: + +let + t = config.nomarchy.theme; + c = t.colors; + inherit (config.nomarchy.lib) rgb rgba; + + # SUPER+1..9 / SUPER+SHIFT+1..9 workspace binds, generated. + workspaceBinds = builtins.concatLists (builtins.genList + (i: + let ws = toString (i + 1); + in [ + "$mod, ${ws}, workspace, ${ws}" + "$mod SHIFT, ${ws}, movetoworkspace, ${ws}" + ]) + 9); +in +{ + wayland.windowManager.hyprland = lib.mkIf config.nomarchy.hyprland.enable { + enable = true; + # The binary and portal come from the NixOS module + # (programs.hyprland.enable); HM only manages configuration. + package = null; + portalPackage = null; + + # HM defaults stateVersion >= 26.05 to the new Lua config and would + # render these hyprlang-style settings as broken `hl.$mod(...)` Lua + # (Hyprland then boots into emergency mode with no binds). + configType = "hyprlang"; + + settings = { + "$mod" = "SUPER"; + "$terminal" = config.nomarchy.terminal; + + monitor = [ ",preferred,auto,1" ]; + + exec-once = [ + # nixpkgs' swww is awww (renamed fork); the binary is awww-daemon. + "awww-daemon" + # Paint the wallpaper as soon as the session is up (waits for + # the daemon internally). + "nomarchy-theme-sync wallpaper" + ]; + + # ── Theme-driven look ────────────────────────────────────────── + general = { + gaps_in = t.ui.gapsIn; + gaps_out = t.ui.gapsOut; + border_size = t.ui.borderSize; + "col.active_border" = "${rgb c.accent} ${rgb c.accentAlt} 45deg"; + "col.inactive_border" = rgb c.overlay; + layout = "dwindle"; + resize_on_border = true; + }; + + decoration = { + rounding = t.ui.rounding; + active_opacity = t.ui.activeOpacity; + inactive_opacity = t.ui.inactiveOpacity; + blur = { + enabled = t.ui.blur; + size = 8; + passes = 2; + popups = true; + }; + shadow = { + enabled = t.ui.shadow; + range = 20; + render_power = 3; + color = rgba c.mantle "aa"; + }; + }; + + animations = { + enabled = true; + bezier = [ + "smooth, 0.25, 0.1, 0.25, 1.0" + "snappy, 0.6, 0.0, 0.1, 1.0" + ]; + animation = [ + "windows, 1, 4, snappy, popin 85%" + "border, 1, 8, smooth" + "fade, 1, 5, smooth" + "workspaces, 1, 4, snappy, slidefade 15%" + ]; + }; + + # ── Behaviour ────────────────────────────────────────────────── + input = { + kb_layout = "us"; + follow_mouse = 1; + touchpad.natural_scroll = true; + }; + + # dwindle:pseudotile was removed in Hyprland 0.55 (the `pseudo` + # dispatcher still exists); setting it aborts config parsing. + dwindle.preserve_split = true; + + misc = { + disable_hyprland_logo = true; + disable_splash_rendering = true; + force_default_wallpaper = 0; + }; + + bind = [ + "$mod, Return, exec, $terminal" + "$mod, D, exec, fuzzel" + "$mod, Q, killactive" + "$mod, F, fullscreen" + "$mod, V, togglefloating" + "$mod SHIFT, E, exit" + + # Theme picker: fuzzel menu over the presets; apply writes the + # state and runs home-manager switch (progress via notify-send). + "$mod, T, exec, nomarchy-theme-sync apply \"$(nomarchy-theme-sync list | fuzzel --dmenu --prompt 'theme> ')\"" + # Cycle the current theme's wallpapers (instant, no rebuild). + "$mod SHIFT, T, exec, nomarchy-theme-sync bg next" + + # Focus + "$mod, H, movefocus, l" + "$mod, L, movefocus, r" + "$mod, K, movefocus, u" + "$mod, J, movefocus, d" + + # Screenshot region to clipboard + ", Print, exec, grim -g \"$(slurp)\" - | wl-copy" + ] ++ workspaceBinds; + + binde = [ + ", XF86AudioRaiseVolume, exec, pamixer -i 5" + ", XF86AudioLowerVolume, exec, pamixer -d 5" + ", XF86AudioMute, exec, pamixer -t" + ", XF86MonBrightnessUp, exec, brightnessctl set +5%" + ", XF86MonBrightnessDown, exec, brightnessctl set 5%-" + ]; + + bindm = [ + "$mod, mouse:272, movewindow" + "$mod, mouse:273, resizewindow" + ]; + }; + }; +} diff --git a/modules/home/options.nix b/modules/home/options.nix new file mode 100644 index 0000000..69eafb8 --- /dev/null +++ b/modules/home/options.nix @@ -0,0 +1,64 @@ +# User-level `nomarchy.*` options — the full surface downstream users +# configure in their home.nix. Kept small on purpose. +{ lib, pkgs, ... }: + +{ + options.nomarchy = { + # ── Required ─────────────────────────────────────────────────── + stateFile = lib.mkOption { + type = lib.types.path; + example = lib.literalExpression "./theme-state.json"; + description = '' + Path to theme-state.json, the single source of truth for all UI + configuration. Must live inside your flake (so evaluation stays + pure) and be git-tracked. nomarchy-theme-sync writes to the + on-disk copy; rebuilds bake it into the generation. + ''; + }; + + # ── Preferences ──────────────────────────────────────────────── + terminal = lib.mkOption { + type = lib.types.str; + default = "ghostty"; + description = "Terminal emulator command, used by keybinds and $TERMINAL."; + }; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.nomarchy-theme-sync; + defaultText = lib.literalExpression "pkgs.nomarchy-theme-sync"; + description = "The nomarchy-theme-sync package (provided by overlays.default)."; + }; + + themesDir = lib.mkOption { + type = lib.types.path; + default = ../../themes; + defaultText = lib.literalExpression "\"\${nomarchy}/themes\""; + description = '' + Theme presets/assets directory probed at eval time for per-theme + app overrides (<slug>/waybar.css, <slug>/waybar.jsonc). Point it + at your own directory to ship custom layouts downstream. + ''; + }; + + # ── Component toggles ────────────────────────────────────────── + hyprland.enable = lib.mkEnableOption "Nomarchy's Hyprland configuration" // { default = true; }; + waybar.enable = lib.mkEnableOption "Nomarchy's Waybar configuration" // { default = true; }; + ghostty.enable = lib.mkEnableOption "Nomarchy's Ghostty configuration" // { default = true; }; + btop.enable = lib.mkEnableOption "btop with the per-theme nomarchy theme" // { default = true; }; + stylix.enable = lib.mkEnableOption "Stylix theming for the long tail of apps (GTK, Qt, cursors)" // { default = true; }; + + # ── Computed (read-only) ─────────────────────────────────────── + theme = lib.mkOption { + type = lib.types.attrs; + readOnly = true; + description = "The parsed theme state (stateFile merged over defaults)."; + }; + + lib = lib.mkOption { + type = lib.types.attrs; + readOnly = true; + description = "Color-format helpers shared by the theme consumers."; + }; + }; +} diff --git a/modules/home/stylix.nix b/modules/home/stylix.nix new file mode 100644 index 0000000..0edb147 --- /dev/null +++ b/modules/home/stylix.nix @@ -0,0 +1,79 @@ +# Stylix — themes the long tail of applications (GTK, Qt, cursors, +# fonts) from the same theme-state.json that drives the live engine. +# +# Division of labour: the hot-reload trio (Hyprland, Waybar, Ghostty) +# is owned by the Nomarchy engine and updates instantly; everything +# Stylix touches updates on the next home-manager switch. That is why +# autoEnable is off and the trio's Stylix targets stay disabled. +# +# Note: the stylix home module itself is imported by homeModules.nomarchy +# in flake.nix (it needs the stylix flake input). +{ config, lib, pkgs, ... }: + +let + cfg = config.nomarchy; + t = cfg.theme; + c = t.colors; + hex = lib.removePrefix "#"; + + # Map the Nomarchy palette onto base16 roles. + base16 = { + base00 = hex c.base; # default background + base01 = hex c.mantle; # darker background (status bars) + base02 = hex c.surface; # selection background + base03 = hex c.muted; # comments + base04 = hex c.subtext; # dark foreground + base05 = hex c.text; # default foreground + base06 = hex c.text; # light foreground + base07 = hex c.text; # lightest foreground + base08 = hex c.bad; # red + base09 = hex c.warn; # orange + base0A = hex c.warn; # yellow + base0B = hex c.good; # green + base0C = hex (builtins.elemAt t.ansi 6); # cyan + base0D = hex c.accent; # blue + base0E = hex c.accentAlt;# magenta + base0F = hex c.bad; # brown/deprecated + }; +in +{ + config = lib.mkIf cfg.stylix.enable { + stylix = { + enable = true; + autoEnable = false; # explicit targets only — the engine owns the rest + polarity = if t.mode == "light" then "light" else "dark"; + base16Scheme = base16; + + targets = { + gtk.enable = true; + qt.enable = true; + }; + + cursor = { + name = lib.mkDefault "Bibata-Modern-Classic"; + package = lib.mkDefault pkgs.bibata-cursors; + size = lib.mkDefault 24; + }; + + fonts = { + monospace = { + name = t.fonts.mono; + package = lib.mkDefault pkgs.nerd-fonts.jetbrains-mono; + }; + sansSerif = { + name = t.fonts.ui; + package = lib.mkDefault pkgs.inter; + }; + serif = { + name = t.fonts.ui; + package = lib.mkDefault pkgs.inter; + }; + emoji = { + name = "Noto Color Emoji"; + package = lib.mkDefault pkgs.noto-fonts-color-emoji; + }; + sizes.terminal = t.fonts.size; + }; + }; + }; +} diff --git a/modules/home/theme.nix b/modules/home/theme.nix new file mode 100644 index 0000000..0710fe8 --- /dev/null +++ b/modules/home/theme.nix @@ -0,0 +1,69 @@ +# Nomarchy theming engine. +# +# nomarchy.stateFile (theme-state.json, inside the consuming flake) is +# ingested at evaluation time — pure, because flake files are store +# paths — and exposed to every other module as `config.nomarchy.theme`. +# +# Theme changes are fully Home Manager managed: `nomarchy-theme-sync +# apply <theme>` writes the JSON and runs `home-manager switch`, baking +# everything (Hyprland, Waybar, Ghostty, btop, Stylix) into one +# read-only generation. No runtime patching, no partial states; theme +# history is generation history. +# +# The one runtime exception is the wallpaper (swww is imperative by +# nature): applied at session start, after every switch (hook below), +# and cycled instantly with `nomarchy-theme-sync bg next`. +{ config, lib, pkgs, ... }: + +let + cfg = config.nomarchy; + + themeState = builtins.fromJSON (builtins.readFile cfg.stateFile); + + # Defaults guarantee evaluation succeeds on a sparse or older state + # file (e.g. one written before a schema field was added). The shipped + # Tokyo Night preset provides the color/ansi fallbacks; the path is + # relative to this module, so it resolves inside Nomarchy's own flake + # source even when consumed downstream. + preset = builtins.fromJSON (builtins.readFile ../../themes/tokyo-night.json); + + defaults = preset // { + fonts = { + mono = "JetBrainsMono Nerd Font"; + ui = "Inter"; + size = 11; + }; + ui = { + gapsIn = 5; + gapsOut = 12; + borderSize = 2; + rounding = 10; + activeOpacity = 1.0; + inactiveOpacity = 0.95; + terminalOpacity = 0.96; + blur = true; + shadow = true; + }; + }; +in +{ + config = { + nomarchy.theme = lib.recursiveUpdate defaults themeState; + + nomarchy.lib = { + # "#7aa2f7" -> "rgb(7aa2f7)" (Hyprland color syntax) + rgb = c: "rgb(${lib.removePrefix "#" c})"; + # "#16161e" -> "rgba(16161eaa)" (Hyprland color + hex alpha) + rgba = c: alpha: "rgba(${lib.removePrefix "#" c}${alpha})"; + }; + + home.packages = [ cfg.package ]; + + # After a switch the new theme's wallpaper should show without + # logging out. Best-effort: outside a graphical session swww isn't + # running and this is a no-op. + home.activation.nomarchyWallpaper = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + run ${lib.getExe cfg.package} --quiet wallpaper || true + ''; + }; +} diff --git a/modules/home/waybar.nix b/modules/home/waybar.nix new file mode 100644 index 0000000..e1bc3e5 --- /dev/null +++ b/modules/home/waybar.nix @@ -0,0 +1,155 @@ +# Waybar — two-tier theming: +# +# 1. Default: structure, fonts, geometry AND palette baked from +# theme-state.json (colors as GTK named colors, @define-color). +# +# 2. Whole-swap: themes with their own visual identity ship +# <themesDir>/<slug>/waybar.css (and optionally waybar.jsonc) which +# replace the generated style/layout entirely. Probed at eval time — +# pure, it's flake source. +{ config, lib, ... }: + +let + t = config.nomarchy.theme; + + # Per-theme override probe. + assetDir = config.nomarchy.themesDir + "/${t.slug}"; + styleOverride = assetDir + "/waybar.css"; + configOverride = assetDir + "/waybar.jsonc"; + hasStyleOverride = builtins.pathExists styleOverride; + hasConfigOverride = builtins.pathExists configOverride; + + # The palette as GTK named colors, straight from the JSON. + colorDefs = lib.concatStringsSep "\n" + (lib.mapAttrsToList (name: value: "@define-color ${name} ${value};") t.colors); + + generatedSettings = { + layer = "top"; + position = "top"; + height = 34; + margin-top = t.ui.gapsOut; + margin-left = t.ui.gapsOut; + margin-right = t.ui.gapsOut; + spacing = 8; + + # waybar re-reads style.css when it changes on disk, so a + # home-manager switch restyles the running bar without a restart. + reload_style_on_change = true; + + modules-left = [ "hyprland/workspaces" "hyprland/window" ]; + modules-center = [ "clock" ]; + modules-right = [ "tray" "pulseaudio" "network" "cpu" "memory" "battery" ]; + + "hyprland/workspaces" = { + format = "{icon}"; + on-click = "activate"; + }; + + "hyprland/window" = { + max-length = 48; + separate-outputs = true; + }; + + clock = { + format = "{:%H:%M}"; + format-alt = "{:%A %d %B %Y}"; + tooltip-format = "<tt><small>{calendar}</small></tt>"; + }; + + pulseaudio = { + format = "{icon} {volume}%"; + format-muted = "󰝟"; + format-icons.default = [ "󰕿" "󰖀" "󰕾" ]; + on-click = "pamixer -t"; + }; + + network = { + format-wifi = "󰤨 {essid}"; + format-ethernet = "󰈀"; + format-disconnected = "󰤭"; + tooltip-format = "{ipaddr} via {gwaddr}"; + }; + + cpu.format = "󰍛 {usage}%"; + memory.format = "󰾆 {percentage}%"; + + battery = { + states = { warning = 25; critical = 10; }; + format = "{icon} {capacity}%"; + format-charging = "󰂄 {capacity}%"; + format-icons = [ "󰁺" "󰁼" "󰁾" "󰂀" "󰁹" ]; + }; + + tray.spacing = 8; + }; + + generatedStyle = '' + /* Palette baked from theme-state.json */ + ${colorDefs} + + * { + font-family: "${t.fonts.ui}", "${t.fonts.mono}"; + font-size: ${toString t.fonts.size}pt; + min-height: 0; + } + + window#waybar { + background: alpha(@base, 0.85); + color: @text; + border: ${toString t.ui.borderSize}px solid alpha(@accent, 0.4); + border-radius: ${toString t.ui.rounding}px; + } + + #workspaces button { + padding: 0 8px; + color: @muted; + border-radius: ${toString t.ui.rounding}px; + } + + #workspaces button.active { + color: @base; + background: @accent; + } + + #workspaces button.urgent { + color: @base; + background: @bad; + } + + #window { + color: @subtext; + padding: 0 12px; + } + + #clock { + color: @text; + font-weight: bold; + } + + #tray, #pulseaudio, #network, #cpu, #memory, #battery { + color: @subtext; + padding: 0 10px; + } + + #pulseaudio.muted { color: @muted; } + #battery.warning { color: @warn; } + #battery.critical { color: @bad; } + #battery.charging { color: @good; } + ''; +in +{ + programs.waybar = lib.mkIf config.nomarchy.waybar.enable { + enable = true; + systemd.enable = true; # started/stopped with graphical-session.target + + settings.mainBar = + if hasConfigOverride + then builtins.fromJSON (builtins.readFile configOverride) + else generatedSettings; + + style = + if hasStyleOverride + then builtins.readFile styleOverride + else generatedStyle; + }; +} diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix new file mode 100644 index 0000000..f4e03f7 --- /dev/null +++ b/modules/nixos/default.nix @@ -0,0 +1,139 @@ +# Nomarchy — reusable system layer (NixOS 26.05). +# +# This module is the distro: import it from any host (see +# nixosModules.nomarchy in flake.nix) and layer your machine specifics +# (bootloader, hostname, users, hardware) on top. Host concerns are +# deliberately NOT set here. Everything user-facing (Hyprland config, +# Waybar, Ghostty, theming) lives in modules/home. +{ config, lib, pkgs, ... }: + +let + cfg = config.nomarchy.system; +in +{ + imports = [ ./options.nix ]; + + config = { + # ── Wayland session: Hyprland ──────────────────────────────────── + # Installs the binary, registers the session, wires up + # xdg-desktop-portal-hyprland. Configuration is Home Manager's job. + programs.hyprland.enable = lib.mkDefault true; + + xdg.portal = { + enable = lib.mkDefault true; + extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; # file pickers, etc. + }; + + services.greetd = lib.mkIf cfg.greeter.enable { + enable = lib.mkDefault true; + settings = { + default_session = { + command = lib.mkDefault "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd Hyprland"; + user = "greeter"; + }; + # Boot straight into the session once; logout → normal greeter. + initial_session = lib.mkIf (cfg.greeter.autoLogin != null) { + command = "Hyprland"; + user = cfg.greeter.autoLogin; + }; + }; + }; + + # ── Audio: Pipewire ────────────────────────────────────────────── + security.rtkit.enable = lib.mkDefault cfg.audio.enable; + services.pulseaudio.enable = lib.mkDefault false; + services.pipewire = lib.mkIf cfg.audio.enable { + enable = lib.mkDefault true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + wireplumber.enable = true; + }; + + # ── Desktop services ───────────────────────────────────────────── + security.polkit.enable = lib.mkDefault true; + services.gnome.gnome-keyring.enable = lib.mkDefault true; + services.dbus.enable = lib.mkDefault true; + services.upower.enable = lib.mkDefault true; + networking.networkmanager.enable = lib.mkDefault true; + + hardware.bluetooth.enable = lib.mkDefault cfg.bluetooth.enable; + services.blueman.enable = lib.mkDefault cfg.bluetooth.enable; + + # ── BTRFS timeline snapshots (ported from the previous iteration) ─ + # Guarded on the actual filesystem so enabling it on an ext4 machine + # is a clean no-op rather than a failing timer. + services.snapper.configs = lib.mkIf + (cfg.snapper.enable && (config.fileSystems."/".fsType or "") == "btrfs") + { + root = { + SUBVOLUME = "/"; + TIMELINE_CREATE = true; + TIMELINE_CLEANUP = true; + TIMELINE_LIMIT_HOURLY = "5"; + TIMELINE_LIMIT_DAILY = "7"; + TIMELINE_LIMIT_WEEKLY = "0"; + TIMELINE_LIMIT_MONTHLY = "0"; + TIMELINE_LIMIT_YEARLY = "0"; + }; + }; + + # ── Fonts ──────────────────────────────────────────────────────── + fonts = { + packages = with pkgs; [ + nerd-fonts.jetbrains-mono + inter + noto-fonts + noto-fonts-color-emoji + ]; + fontconfig.defaultFonts = { + monospace = lib.mkDefault [ "JetBrainsMono Nerd Font" ]; + sansSerif = lib.mkDefault [ "Inter" ]; + emoji = lib.mkDefault [ "Noto Color Emoji" ]; + }; + }; + + # ── Essential packages ─────────────────────────────────────────── + environment.systemPackages = with pkgs; [ + nomarchy-theme-sync # provided by overlays.default + git + vim + wget + curl + jq + brightnessctl + playerctl + pamixer + wl-clipboard + grim + slurp + ] ++ lib.optional (cfg.snapper.enable && (config.fileSystems."/".fsType or "") == "btrfs") + # Snapshot, then rebuild — rollback material for system changes + # (theme changes don't need it; HM generations already roll back). + (pkgs.writeShellScriptBin "nixos-rebuild-snap" '' + if [ "$(id -u)" -ne 0 ]; then + echo "This script must be run as root (use sudo)" >&2 + exit 1 + fi + echo "Creating pre-rebuild snapshot..." + ${pkgs.snapper}/bin/snapper -c root create \ + -d "Pre-rebuild $(date +'%Y-%m-%d %H:%M:%S')" \ + --cleanup-algorithm number + echo "Rebuilding..." + nixos-rebuild switch --flake /etc/nixos#default "$@" + ''); + + # ── Nix itself ─────────────────────────────────────────────────── + nix = { + settings = { + experimental-features = [ "nix-command" "flakes" ]; + auto-optimise-store = lib.mkDefault true; + }; + gc = { + automatic = lib.mkDefault true; + dates = lib.mkDefault "weekly"; + options = lib.mkDefault "--delete-older-than 14d"; + }; + }; + }; +} diff --git a/modules/nixos/options.nix b/modules/nixos/options.nix new file mode 100644 index 0000000..2b93139 --- /dev/null +++ b/modules/nixos/options.nix @@ -0,0 +1,33 @@ +# System-level `nomarchy.system.*` options. +# +# Deliberately small: only things a downstream user plausibly disagrees +# with get a toggle. Everything else in the system module is set with +# lib.mkDefault, so plain NixOS options override it natively. +{ lib, ... }: + +{ + options.nomarchy.system = { + greeter.enable = lib.mkEnableOption "the greetd/tuigreet login screen" // { default = true; }; + + greeter.autoLogin = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "ada"; + description = '' + Log this user straight into Hyprland on boot (greetd + initial_session); logging out lands on the normal greeter. + The installer sets it on LUKS-encrypted machines — the disk + passphrase already gates access, a second prompt is ceremony. + ''; + }; + + audio.enable = lib.mkEnableOption "the Pipewire audio stack" // { default = true; }; + bluetooth.enable = lib.mkEnableOption "Bluetooth support with blueman" // { default = true; }; + + snapper.enable = lib.mkEnableOption '' + hourly/daily BTRFS timeline snapshots of / via snapper, plus the + `nixos-rebuild-snap` pre-rebuild-snapshot helper. No-op unless the + root filesystem is BTRFS with a /.snapshots subvolume (the installer + creates one)''; + }; +} diff --git a/pkgs/nomarchy-install/compose-lock.py b/pkgs/nomarchy-install/compose-lock.py new file mode 100644 index 0000000..c8f8193 --- /dev/null +++ b/pkgs/nomarchy-install/compose-lock.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Compose a flake.lock for the freshly installed machine — offline. + +The generated downstream flake has exactly one input, `nomarchy`. A normal +`nix flake lock` would fetch it from its forge; on an offline install we +already KNOW the answer: the live ISO was built from a specific nomarchy +rev whose source (and all transitive inputs, via the ISO's pinned store +paths) is on hand. So we write the lock nix would have written: + + root ─▶ nomarchy (locked to the baked rev+narHash) + └─▶ every node from nomarchy's own flake.lock, verbatim + +Nix resolves locked inputs by narHash against the store before touching +the network, so evaluation on the target works without connectivity. + +Usage: + compose-lock.py <nomarchy-flake.lock> <out> <locked-json> <original-json> + +where <locked-json>/<original-json> are the flake-lock node fields for the +nomarchy input (any input type — github, git, …), baked at package build. +""" +import json +import sys + + +def main() -> None: + src_lock, out, locked_json, original_json = sys.argv[1:5] + + locked = json.loads(locked_json) + if not locked.get("narHash"): + sys.exit("compose-lock: no narHash in locked metadata") + + with open(src_lock) as f: + upstream = json.load(f) + + nodes = dict(upstream["nodes"]) + # nomarchy's root node describes ITS inputs; that mapping becomes the + # input set of the new `nomarchy` node. + nomarchy_inputs = nodes.pop("root")["inputs"] + if "nomarchy" in nodes: + sys.exit("compose-lock: upstream lock already has a 'nomarchy' node") + + # Follows paths (list-valued inputs) are absolute from the lock's + # root; the copied nodes now live one level down, so ["nixpkgs"] + # must become ["nomarchy", "nixpkgs"]. Plain string values reference + # node keys directly and stay as they are. + for node in nodes.values(): + for name, ref in node.get("inputs", {}).items(): + if isinstance(ref, list): + node["inputs"][name] = ["nomarchy"] + ref + + nodes["nomarchy"] = { + "inputs": nomarchy_inputs, + "locked": locked, + "original": json.loads(original_json), + } + nodes["root"] = {"inputs": {"nomarchy": "nomarchy"}} + + with open(out, "w") as f: + json.dump({"nodes": nodes, "root": "root", "version": 7}, f, indent=2) + f.write("\n") + pin = locked.get("rev", locked["narHash"])[:12] + print(f"compose-lock: wrote {out} (nomarchy @ {pin})") + + +if __name__ == "__main__": + main() diff --git a/pkgs/nomarchy-install/default.nix b/pkgs/nomarchy-install/default.nix new file mode 100644 index 0000000..7ad21f5 --- /dev/null +++ b/pkgs/nomarchy-install/default.nix @@ -0,0 +1,78 @@ +{ lib +, stdenvNoCC +, makeWrapper +, bash +, gum +, disko +, whois # mkpasswd +, git +, python3 +, util-linux # lsblk, wipefs, swapoff, lscpu +, gptfdisk # sgdisk +, parted # partprobe +, cryptsetup +, lvm2 # dmsetup +, pciutils # lspci +, btrfs-progs # inspect-internal map-swapfile (hibernation offset) + # Baked metadata — what this installer installs and where it came from. +, templateDir # templates/downstream (home.nix, theme-state.json) +, nomarchyLock # the distro's flake.lock (for offline lock composition) +, hardwareModuleNames # newline-separated nixos-hardware module names +, nixpkgsPath # pinned nixpkgs source (NIX_PATH for disko's eval) +, flakeUrl # flake-style URL written into the generated flake.nix +, lockedNode # attrset: the flake.lock "locked" node for nomarchy + # (path-type: resolves from the ISO store, offline) +, originalNode # attrset: the flake.lock "original" node (the forge, + # so `nix flake update` later re-resolves normally) +, rev ? null # informational: rev the ISO was built from +}: + +stdenvNoCC.mkDerivation { + pname = "nomarchy-install"; + version = "0.1.0"; + + src = ./.; + + nativeBuildInputs = [ makeWrapper ]; + + installPhase = '' + runHook preInstall + + install -Dm755 nomarchy-install.sh $out/bin/nomarchy-install + patchShebangs $out/bin/nomarchy-install + + share=$out/share/nomarchy-install + install -Dm644 disko-config.nix "$share/disko-config.nix" + install -Dm644 hardware-db.sh "$share/hardware-db.sh" + install -Dm644 compose-lock.py "$share/compose-lock.py" + install -Dm644 ${nomarchyLock} "$share/flake.lock" + install -Dm644 ${hardwareModuleNames} "$share/hardware-modules.txt" + # Empty flake registry: no network lookups for indirect refs. + echo '{"flakes":[],"version":2}' > "$share/registry.json" + mkdir -p "$share/template" + cp ${templateDir}/home.nix ${templateDir}/theme-state.json "$share/template/" + + # nixos-install / nixos-generate-config / nixos-enter / nix / systemd + # tools come from the live system on purpose — they must match it. + wrapProgram $out/bin/nomarchy-install \ + --prefix PATH : ${lib.makeBinPath [ + bash gum disko whois git python3 + util-linux gptfdisk parted cryptsetup lvm2 pciutils btrfs-progs + ]} \ + --set NOMARCHY_INSTALL_SHARE "$share" \ + --set NOMARCHY_NIXPKGS ${nixpkgsPath} \ + --set NOMARCHY_FLAKE_URL ${lib.escapeShellArg flakeUrl} \ + --set NOMARCHY_REV ${lib.escapeShellArg (toString rev)} \ + --set NOMARCHY_LOCKED_JSON ${lib.escapeShellArg (builtins.toJSON lockedNode)} \ + --set NOMARCHY_ORIGINAL_JSON ${lib.escapeShellArg (builtins.toJSON originalNode)} + + runHook postInstall + ''; + + meta = { + description = "Nomarchy guided installer (disko + mkFlake + nixos-install)"; + license = lib.licenses.mit; + mainProgram = "nomarchy-install"; + platforms = lib.platforms.linux; + }; +} diff --git a/pkgs/nomarchy-install/disko-config.nix b/pkgs/nomarchy-install/disko-config.nix new file mode 100644 index 0000000..c78e9b0 --- /dev/null +++ b/pkgs/nomarchy-install/disko-config.nix @@ -0,0 +1,81 @@ +# Nomarchy installer disk layout — single disk, GPT, 1 GiB ESP, BTRFS +# root with subvolumes, optionally under LUKS2. Invoked by +# nomarchy-install via: +# +# disko --mode destroy,format,mount \ +# --argstr mainDrive /dev/nvme0n1 \ +# --arg withLuks true \ +# --argstr swapSize 16G \ +# disko-config.nix +# +# Ported from the previous iteration's golden-path config (multi-disk +# RAID and the impermanence snapshot dropped for v1 — see git history +# of installer/disko-config.nix at 3bdfc35 when adding them back). +# Function arguments, not sed-templating: escaping shell into Nix +# proved fragile twice before. +{ mainDrive +, withLuks ? true +, swapSize ? "0" # "0" = no swap; otherwise e.g. "16G" (sized for hibernation) +, ... +}: + +let + btrfsMountOptions = [ "compress=zstd" "noatime" ]; + + rootBtrfs = { + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "@" = { mountpoint = "/"; mountOptions = btrfsMountOptions; }; + "@home" = { mountpoint = "/home"; mountOptions = btrfsMountOptions; }; + "@nix" = { mountpoint = "/nix"; mountOptions = btrfsMountOptions; }; + "@log" = { mountpoint = "/var/log"; mountOptions = btrfsMountOptions; }; + # snapper timeline snapshots (nomarchy.system.snapper.enable) + "@snapshots" = { mountpoint = "/.snapshots"; mountOptions = btrfsMountOptions; }; + } // (if swapSize == "0" then { } else { + # Hibernation-ready swapfile on its own subvolume; disko's + # mkswapfile handles the BTRFS NOCOW requirements. + "@swap" = { + mountpoint = "/swap"; + swap.swapfile.size = swapSize; + }; + }); + }; +in +{ + disko.devices.disk.main = { + type = "disk"; + device = mainDrive; + content = { + type = "gpt"; + partitions = { + # 1 GiB ESP — fits several kernel generations + initrd. + ESP = { + priority = 1; + name = "ESP"; + start = "1M"; + end = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "umask=0077" ]; + }; + }; + root = { + size = "100%"; + content = + if withLuks then { + type = "luks"; + name = "crypted"; + passwordFile = "/tmp/nomarchy-luks.key"; + settings.allowDiscards = true; + content = rootBtrfs; + } else + rootBtrfs; + }; + }; + }; + }; +} diff --git a/pkgs/nomarchy-install/hardware-db.sh b/pkgs/nomarchy-install/hardware-db.sh new file mode 100644 index 0000000..acc89c4 --- /dev/null +++ b/pkgs/nomarchy-install/hardware-db.sh @@ -0,0 +1,179 @@ +# Nomarchy hardware database — sourced by nomarchy-install. +# +# Flat table mapping detected DMI fields to nixos-hardware module names +# (the strings nomarchy.lib.mkFlake's hardwareProfile accepts). Ported +# from the previous iteration (installer/hardware-db.sh at 3bdfc35). +# +# Format (pipe-separated): +# sys_vendor_regex | product_regex | nixos_hardware_module +# +# - Matching is bash `[[ =~ ]]` regex, case-insensitive. +# - First match wins — put more specific entries (e.g. exact model years) +# above broader fallbacks. +# +# To add a new device: grab `/sys/class/dmi/id/product_name` on the +# target machine and add a line. Module names must exist in +# nixos-hardware.nixosModules (mkFlake fails with suggestions if not). + +HARDWARE_DB=( + # Framework --------------------------------------------------------------- + # Module names follow nixos-hardware's actual attrs — for Framework 13 + # the per-generation modules dropped the "13-" prefix. + "Framework|Laptop 16.*AMD|framework-16-7040-amd" + "Framework|Laptop 16.*Ryzen AI 300|framework-16-amd-ai-300-series" + "Framework|Laptop 13.*Ryzen AI 300|framework-amd-ai-300-series" + "Framework|Laptop 13.*Ryzen 7040|framework-13-7040-amd" + "Framework|Laptop 13.*Core Ultra|framework-intel-core-ultra-series1" + "Framework|Laptop 13.*13th Gen Intel|framework-13th-gen-intel" + "Framework|Laptop 13.*12th Gen Intel|framework-12th-gen-intel" + "Framework|Laptop 13.*11th Gen Intel|framework-11th-gen-intel" + "Framework|Laptop \(13.*|framework" + + # Dell XPS / Precision / Latitude ---------------------------------------- + "Dell|XPS 15 9500|dell-xps-15-9500" + "Dell|XPS 15 9510|dell-xps-15-9510" + "Dell|XPS 15 9520|dell-xps-15-9520" + "Dell|XPS 13 9310|dell-xps-13-9310" + "Dell|XPS 13 9370|dell-xps-13-9370" + "Dell|XPS 13 9380|dell-xps-13-9380" + "Dell|XPS 13 7390|dell-xps-13-7390" + "Dell|Precision 5530|dell-precision-5530" + "Dell|Latitude 7490|dell-latitude-7490" + "Dell|Latitude 7430|dell-latitude-7430" + "Dell|Latitude 7420|dell-latitude-7420" + + # Lenovo ThinkPad -------------------------------------------------------- + # X1 Carbon: the per-gen modules are named "x1-Nth-gen", not + # "x1-carbon-genN" — the X1 series IS the Carbon line. + "LENOVO|ThinkPad X1 Carbon Gen 11|lenovo-thinkpad-x1-11th-gen" + "LENOVO|ThinkPad X1 Carbon Gen 10|lenovo-thinkpad-x1-10th-gen" + "LENOVO|ThinkPad X1 Carbon Gen 9|lenovo-thinkpad-x1-9th-gen" + "LENOVO|ThinkPad X1 Carbon Gen 7|lenovo-thinkpad-x1-7th-gen" + "LENOVO|ThinkPad X1 Carbon Gen 6|lenovo-thinkpad-x1-6th-gen" + "LENOVO|ThinkPad X1 Extreme|lenovo-thinkpad-x1-extreme" + "LENOVO|ThinkPad X1 Nano|lenovo-thinkpad-x1-nano-gen1" + "LENOVO|ThinkPad T14 Gen 3|lenovo-thinkpad-t14-amd-gen3" + "LENOVO|ThinkPad T14 Gen 2|lenovo-thinkpad-t14-amd-gen2" + "LENOVO|ThinkPad T14 Gen 1|lenovo-thinkpad-t14-amd-gen1" + "LENOVO|ThinkPad T480|lenovo-thinkpad-t480" + "LENOVO|ThinkPad L13|lenovo-thinkpad-l13" + "LENOVO|ThinkPad P14s.*Gen 5|lenovo-thinkpad-p14s-amd-gen5" + "LENOVO|ThinkPad P14s.*Gen 4|lenovo-thinkpad-p14s-amd-gen4" + "LENOVO|ThinkPad P14s.*Gen 3|lenovo-thinkpad-p14s-amd-gen3" + + # Microsoft Surface ------------------------------------------------------ + # nixos-hardware ships per-chip modules, not per-revision. + "Microsoft|Surface Pro 10|microsoft-surface-pro-intel" + "Microsoft|Surface Pro 9|microsoft-surface-pro-9" + "Microsoft|Surface Pro 8|microsoft-surface-pro-intel" + "Microsoft|Surface Pro 7|microsoft-surface-pro-intel" + "Microsoft|Surface Pro 6|microsoft-surface-pro-intel" + "Microsoft|Surface Pro 3|microsoft-surface-pro-3" + "Microsoft|Surface Laptop.*Ryzen|microsoft-surface-laptop-amd" + "Microsoft|Surface Go|microsoft-surface-go" + + # ASUS ROG Ally / Zephyrus / Strix --------------------------------------- + "ASUS.*|RC71L|asus-ally-rc71l" + "ASUS.*|RC72LA|asus-ally-rc71l" + "ASUS.*|ROG Ally|asus-ally-rc71l" + "ASUS.*|ROG Zephyrus G14.*GA402X|asus-zephyrus-ga402x" + "ASUS.*|ROG Zephyrus G14.*GA402|asus-zephyrus-ga402" + "ASUS.*|ROG Zephyrus G14.*GA401|asus-zephyrus-ga401" + "ASUS.*|ROG Zephyrus G15|asus-zephyrus-ga503" + "ASUS.*|ROG Zephyrus.*GU603|asus-zephyrus-gu603h" + "ASUS.*|ROG Strix G513|asus-rog-strix-g513im" + "ASUS.*|ROG Strix G533|asus-rog-strix-g533zw" + + # Apple (T2 Intel; M-series is aarch64 — out of scope) ------------------- + "Apple.*|MacBookPro15|apple-t2" + "Apple.*|MacBookPro16|apple-t2" + "Apple.*|MacBookAir8|apple-t2" + "Apple.*|MacBookAir9|apple-t2" + "Apple.*|iMac19|apple-t2" + "Apple.*|iMacPro1|apple-t2" + + # System76 --------------------------------------------------------------- + "System76|Oryx Pro.*|system76" + "System76|Lemur Pro.*|system76" + "System76|Darter Pro.*|system76" + "System76|Galago Pro.*|system76" + "System76|Pangolin.*|system76" + + # Known-unsupported (so contributors know they're not missing): + # - Valve Steam Deck: Jovian-NixOS territory, not nixos-hardware. + # - Snapdragon X laptops / Raspberry Pi: aarch64; installer is x86_64. +) + +# ---------------------------------------------------------------------------- +# nomarchy_detect_hw — inspects DMI + lspci + /sys, emits one line per hit: +# MODULE <nixos-hardware module name> +# DETAIL <human-readable explanation> +# Returns 0 if at least one module was detected. +# ---------------------------------------------------------------------------- +nomarchy_detect_hw() { + local sys_vendor product_name cpu_vendor + sys_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || echo "") + product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "") + cpu_vendor=$(lscpu 2>/dev/null | awk -F: '/Vendor ID/{gsub(/ /,"",$2); print $2; exit}') + + local detected=0 + + # CPU + case "$cpu_vendor" in + AuthenticAMD) echo "MODULE common-cpu-amd"; echo "DETAIL cpu: AMD"; detected=1 ;; + GenuineIntel) echo "MODULE common-cpu-intel"; echo "DETAIL cpu: Intel"; detected=1 ;; + esac + + # GPU (lspci may list several; report all) + if command -v lspci >/dev/null 2>&1; then + local gpu_line nvidia=0 amdgpu=0 intelgpu=0 + while IFS= read -r gpu_line; do + case "$gpu_line" in + *"[10de:"*|*"NVIDIA"*) nvidia=1 ;; + *"[1002:"*|*"AMD/ATI"*|*"Advanced Micro Devices"*) amdgpu=1 ;; + *"[8086:"*|*"Intel Corporation"*) intelgpu=1 ;; + esac + done < <(lspci -nn 2>/dev/null | grep -iE 'vga|3d|display') + + (( nvidia )) && { echo "MODULE common-gpu-nvidia"; echo "DETAIL gpu: NVIDIA"; detected=1; } + (( amdgpu )) && { echo "MODULE common-gpu-amd"; echo "DETAIL gpu: AMD"; detected=1; } + (( intelgpu )) && { echo "MODULE common-gpu-intel"; echo "DETAIL gpu: Intel"; detected=1; } + fi + + # Chassis (glob test, not compgen — nixpkgs' non-interactive bash + # is built without the completion builtins) + local bats=(/sys/class/power_supply/BAT*) + if [[ -e "${bats[0]}" ]]; then + echo "MODULE common-pc-laptop" + echo "DETAIL chassis: laptop (battery present)" + else + echo "MODULE common-pc" + echo "DETAIL chassis: desktop" + fi + detected=1 + + # SSD + local rotational + rotational=$(cat /sys/block/nvme0n1/queue/rotational 2>/dev/null \ + || cat /sys/block/sda/queue/rotational 2>/dev/null || echo "") + if [[ "$rotational" == "0" ]]; then + echo "MODULE common-pc-ssd" + echo "DETAIL storage: SSD" + fi + + # Model-specific match from the DB + local entry v_re p_re mod + for entry in "${HARDWARE_DB[@]}"; do + IFS='|' read -r v_re p_re mod <<< "$entry" + shopt -s nocasematch + if [[ "$sys_vendor" =~ $v_re ]] && [[ "$product_name" =~ $p_re ]]; then + shopt -u nocasematch + echo "MODULE $mod" + echo "DETAIL model: $sys_vendor $product_name → $mod" + break + fi + shopt -u nocasematch + done + + return $(( 1 - detected )) +} diff --git a/pkgs/nomarchy-install/nomarchy-install.sh b/pkgs/nomarchy-install/nomarchy-install.sh new file mode 100644 index 0000000..8ea40b6 --- /dev/null +++ b/pkgs/nomarchy-install/nomarchy-install.sh @@ -0,0 +1,511 @@ +#!/usr/bin/env bash +# nomarchy-install — guided installer for the Nomarchy live ISO. +# +# Partitions one disk (GPT + 1 GiB ESP + BTRFS, optional LUKS2) with disko, +# generates a downstream machine flake at /home/<user>/.nomarchy (one +# nomarchy.lib.mkFlake call — the flake the user never hand-edits), and +# runs nixos-install. Works offline: the ISO pins every flake input, and +# the target flake.lock is composed from the rev the ISO was built from. +# +# Ported from the previous iteration's installer/install.sh (3bdfc35) — +# the pre-wipe and ordering comments carry its hard-won fixes. v1 scope: +# single disk, UEFI only. Multi-disk RAID and impermanence live in git +# history when they're wanted back. +# +# Unattended mode (for CI/VM tests): +# NOMARCHY_UNATTENDED=1 NOMARCHY_DISK=/dev/vda NOMARCHY_USERNAME=me \ +# NOMARCHY_PASSWORD=secret [NOMARCHY_HOSTNAME=nomarchy] \ +# [NOMARCHY_TIMEZONE=UTC] [NOMARCHY_LUKS_PASSPHRASE=...] \ +# [NOMARCHY_SWAP_GB=N (default: RAM size; 0 = none)] \ +# [NOMARCHY_HW="auto"|"none"|"mod1 mod2"] [NOMARCHY_FINISH=none|reboot|poweroff] +# nomarchy-install + +set -euo pipefail + +# Baked in by the package wrapper: +# NOMARCHY_INSTALL_SHARE — disko-config.nix, hardware-db.sh, +# compose-lock.py, flake.lock, template/, +# hardware-modules.txt +# NOMARCHY_FLAKE_URL — flake URL written into the generated flake.nix +# NOMARCHY_REV — nomarchy rev this ISO was built from ("" if dirty) +# NOMARCHY_LOCKED_JSON / NOMARCHY_ORIGINAL_JSON — flake.lock node fields +SHARE="${NOMARCHY_INSTALL_SHARE:?not run via the packaged wrapper}" + +UNATTENDED="${NOMARCHY_UNATTENDED:-0}" +LUKS_KEY_PATH="/tmp/nomarchy-luks.key" + +# ─── UI helpers ───────────────────────────────────────────────────────── +header() { gum style --border rounded --padding "0 2" --margin "1 0" \ + --border-foreground 212 "$@"; } +section() { gum style --foreground 212 --bold "── $* ──"; } +info() { gum style --foreground 245 " $*"; } +success() { gum style --foreground 42 " ✓ $*"; } +warn() { gum style --foreground 214 " ⚠ $*"; } +fail() { gum style --foreground 9 " ✗ $*"; exit 1; } + +confirm() { # confirm <prompt> — auto-yes when unattended + [[ "$UNATTENDED" == "1" ]] && return 0 + gum confirm "$1" +} + +# ─── Environment checks ───────────────────────────────────────────────── +if [[ $EUID -ne 0 ]]; then + exec sudo --preserve-env "$0" "$@" +fi + +header "Nomarchy installer" "NixOS, themed and ready to go." + +[[ -d /sys/firmware/efi ]] \ + || fail "No UEFI firmware detected. v1 installs systemd-boot and needs UEFI (BIOS/legacy: see roadmap)." +command -v nixos-install >/dev/null \ + || fail "nixos-install not found — run this from the Nomarchy live ISO." +grep -q nomarchy-live /etc/hostname 2>/dev/null \ + || warn "This doesn't look like the Nomarchy live ISO; proceeding anyway." + +# Offline-proof nix: disko's eval resolves <nixpkgs> (a dead channel on +# the live ISO) and flake commands may consult the global registry — +# point both at what the ISO already carries. +export NIX_PATH="nixpkgs=$NOMARCHY_NIXPKGS" +export NIX_CONFIG="flake-registry = $SHARE/registry.json" + +# With no network, every substituter query is a DNS timeout + retry storm +# (and tickles a nix goal.cc assertion crash). Everything an install needs +# is pinned into the ISO — drop the binary caches and substitute from the +# live store's daemon instead. The explicit daemon substituter matters: +# nixos-install builds with --store /mnt and its own "auto?trusted=1" +# substituter resolves to the TARGET store (i.e. itself), so without +# this nothing flows from the ISO and nix bootstraps gcc from source. +NIXOS_INSTALL_OPTS=() +if ! timeout 3 bash -c '</dev/tcp/cache.nixos.org/443' 2>/dev/null; then + info "No network — substituting from the ISO store only." + NIX_CONFIG+=$'\nsubstituters =\nextra-substituters = daemon?trusted=1\nbuilders =' + # Must ALSO go through nixos-install as a flag: it passes its own + # --extra-substituters "auto?trusted=1", and flags override the env + # config for the same setting — without this the in-target build + # substitutes from itself (= nothing) and bootstraps gcc from source. + NIXOS_INSTALL_OPTS+=(--substituters "daemon?trusted=1") +fi + +# ─── Disk selection ───────────────────────────────────────────────────── +section "Target disk" + +# Never offer the medium we're running from. +live_disk="" +live_src=$(findmnt -no SOURCE /iso 2>/dev/null || true) +[[ -n "$live_src" ]] && live_disk=$(lsblk -no PKNAME "$live_src" 2>/dev/null || true) + +mapfile -t disks < <(lsblk -dpno NAME,SIZE,MODEL,TYPE \ + | awk -v skip="/dev/${live_disk:-NONE}" \ + '$NF == "disk" && $1 != skip && $1 !~ /loop|zram|sr[0-9]/ {NF--; print}') +[[ ${#disks[@]} -gt 0 ]] || fail "No installable disks found." + +if [[ "$UNATTENDED" == "1" ]]; then + TARGET_DISK="${NOMARCHY_DISK:?NOMARCHY_DISK required in unattended mode}" +else + choice=$(printf '%s\n' "${disks[@]}" \ + | gum choose --header "Install Nomarchy on which disk? (EVERYTHING on it will be erased)") + TARGET_DISK="${choice%% *}" +fi +[[ -b "$TARGET_DISK" ]] || fail "$TARGET_DISK is not a block device." +info "Target: $TARGET_DISK" + +# ─── Encryption ───────────────────────────────────────────────────────── +section "Disk encryption" + +# LUKS is the default: full-disk encryption, and in exchange the machine +# logs you straight into the desktop (the passphrase already gates access). +LUKS_PASSPHRASE="" +if [[ "$UNATTENDED" == "1" ]]; then + LUKS_PASSPHRASE="${NOMARCHY_LUKS_PASSPHRASE:-}" +elif gum confirm --default=yes "Encrypt the disk with LUKS? (default — also enables passwordless desktop login)"; then + while true; do + p1=$(gum input --password --placeholder "LUKS passphrase (min 8 chars)") + [[ ${#p1} -ge 8 ]] || { warn "Too short."; continue; } + p2=$(gum input --password --placeholder "Repeat passphrase") + [[ "$p1" == "$p2" ]] && { LUKS_PASSPHRASE="$p1"; break; } + warn "Passphrases don't match." + done +fi +WITH_LUKS=false; [[ -n "$LUKS_PASSPHRASE" ]] && WITH_LUKS=true +info "Encryption: $([[ $WITH_LUKS == true ]] && echo "LUKS2 (desktop auto-login)" || echo none)" + +# ─── Swap / hibernation ───────────────────────────────────────────────── +# A swapfile ≥ RAM on its own BTRFS subvolume makes hibernation possible; +# the resume offset is wired into the config below. +ram_gb=$(awk '/MemTotal/ {print int(($2 + 1048575) / 1048576)}' /proc/meminfo) +if [[ "$UNATTENDED" == "1" ]]; then + SWAP_GB="${NOMARCHY_SWAP_GB:-$ram_gb}" +else + SWAP_GB=$(gum input --value "$ram_gb" \ + --placeholder "swap size in GiB (≥ RAM enables hibernation, 0 = none)") +fi +[[ "$SWAP_GB" =~ ^[0-9]+$ ]] || fail "Swap size must be a whole number of GiB." +info "Swap: $([[ "$SWAP_GB" == "0" ]] && echo none || echo "${SWAP_GB}G swapfile (hibernation-ready)")" + +# ─── User account ─────────────────────────────────────────────────────── +section "Your account" + +if [[ "$UNATTENDED" == "1" ]]; then + USERNAME="${NOMARCHY_USERNAME:?}" + PASSWORD="${NOMARCHY_PASSWORD:?}" + HOSTNAME_="${NOMARCHY_HOSTNAME:-nomarchy}" + TIMEZONE="${NOMARCHY_TIMEZONE:-UTC}" +else + while true; do + USERNAME=$(gum input --placeholder "username (lowercase, e.g. ada)") + [[ "$USERNAME" =~ ^[a-z_][a-z0-9_-]*$ ]] && break + warn "Invalid username (lowercase letters, digits, - and _)." + done + while true; do + PASSWORD=$(gum input --password --placeholder "password for $USERNAME") + [[ -n "$PASSWORD" ]] || { warn "Empty password."; continue; } + p2=$(gum input --password --placeholder "repeat password") + [[ "$PASSWORD" == "$p2" ]] && break + warn "Passwords don't match." + done + while true; do + HOSTNAME_=$(gum input --value "nomarchy" --placeholder "hostname") + [[ "$HOSTNAME_" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$ ]] && break + warn "Invalid hostname." + done + TIMEZONE=$(timedatectl list-timezones | gum filter --placeholder "timezone (type to search)" || echo UTC) +fi +[[ "$USERNAME" =~ ^[a-z_][a-z0-9_-]*$ ]] || fail "Invalid username '$USERNAME'." +[[ -f "/usr/share/zoneinfo/$TIMEZONE" || -e "/etc/zoneinfo/$TIMEZONE" ]] \ + || timedatectl list-timezones 2>/dev/null | grep -qx "$TIMEZONE" \ + || warn "Timezone '$TIMEZONE' not verifiable; continuing." +HASHED_PASSWORD=$(printf '%s' "$PASSWORD" | mkpasswd -m sha-512 -s) +unset PASSWORD +info "User: $USERNAME @ $HOSTNAME_ ($TIMEZONE)" + +# ─── Hardware profile ─────────────────────────────────────────────────── +section "Hardware detection" + +# shellcheck source=hardware-db.sh +source "$SHARE/hardware-db.sh" + +HW_PROFILES=() +hw_mode="${NOMARCHY_HW:-auto}" +if [[ "$hw_mode" == "none" ]]; then + info "Hardware profiles skipped." +elif [[ "$hw_mode" != "auto" && "$UNATTENDED" == "1" ]]; then + read -ra HW_PROFILES <<< "$hw_mode" +else + detection=$(nomarchy_detect_hw || true) + if [[ -n "$detection" ]]; then + while IFS= read -r line; do + case "$line" in + MODULE\ *) HW_PROFILES+=("${line#MODULE }") ;; + DETAIL\ *) info "→ ${line#DETAIL }" ;; + esac + done <<< "$detection" + fi + if [[ "$UNATTENDED" != "1" ]]; then + if [[ ${#HW_PROFILES[@]} -gt 0 ]] \ + && ! gum confirm "Use these nixos-hardware profiles: ${HW_PROFILES[*]}?"; then + HW_PROFILES=() + picked=$(gum filter --no-limit \ + --placeholder "pick profiles manually (tab to select, enter to finish, esc for none)" \ + < "$SHARE/hardware-modules.txt" || true) + [[ -n "$picked" ]] && mapfile -t HW_PROFILES <<< "$picked" + fi + fi +fi +info "Profiles: ${HW_PROFILES[*]:-(none)}" + +# ─── Review & point of no return ──────────────────────────────────────── +section "Review" + +gum style --border normal --padding "0 2" \ + "Disk: $TARGET_DISK (WILL BE ERASED)" \ + "Encryption: $([[ $WITH_LUKS == true ]] && echo "LUKS2 + desktop auto-login" || echo none)" \ + "Swap: $([[ "$SWAP_GB" == "0" ]] && echo none || echo "${SWAP_GB}G (hibernation)")" \ + "User: $USERNAME" \ + "Hostname: $HOSTNAME_" \ + "Timezone: $TIMEZONE" \ + "Hardware: ${HW_PROFILES[*]:-none}" \ + "Snapshots: snapper timeline on /" \ + "Source: nomarchy ${NOMARCHY_REV:0:12}${NOMARCHY_REV:+ }$([[ -z "${NOMARCHY_REV:-}" ]] && echo "(dirty tree) ")— pinned into the ISO, no network needed" + +if [[ "$UNATTENDED" != "1" ]]; then + typed=$(gum input --placeholder "type the disk name ($(basename "$TARGET_DISK")) to confirm the wipe") + [[ "$typed" == "$(basename "$TARGET_DISK")" ]] || fail "Confirmation mismatch — aborting, nothing touched." +fi + +# ─── Partitioning ─────────────────────────────────────────────────────── +section "Partitioning" + +# Pre-wipe: disko gates destructive steps on blkid — on a previously +# installed disk it would overlay the old GPT and skip mkfs on a stale +# ESP ("wrong fs type, bad superblock" at mount). Wipe first. Teardown +# order matters: mounts → swap → LUKS mappings → signatures. +prewipe() { + local drive="$1" name backing part + info "Pre-wiping $drive..." + umount -R /mnt 2>/dev/null || true + swapoff -a 2>/dev/null || true + if command -v dmsetup >/dev/null 2>&1; then + while read -r name _; do + [[ -n "$name" && "$name" != "No" ]] || continue + backing=$(cryptsetup status "$name" 2>/dev/null \ + | awk '/^[[:space:]]*device:/ { print $2; exit }') || continue + [[ "$backing" == "$drive"* ]] || continue + info "closing stale LUKS mapping $name" + cryptsetup close "$name" + done < <(dmsetup ls --target crypt 2>/dev/null) + fi + for part in "${drive}"?*; do + # unmatched glob stays literal; -b filters it out + [[ -b "$part" ]] || continue + wipefs -af "$part" >/dev/null + done + wipefs -af "$drive" >/dev/null + sgdisk --zap-all "$drive" >/dev/null + # 16 MiB covers LUKS2 headers and the first BTRFS superblock — + # wipefs alone misses damaged variants. + dd if=/dev/zero of="$drive" bs=1M count=16 conv=fsync status=none + partprobe "$drive" 2>/dev/null || true + udevadm settle --timeout=30 || info "udevadm settle timed out; continuing." + if lsblk -no MOUNTPOINTS "$drive" 2>/dev/null | grep -qE '\S'; then + fail "$drive still has active mountpoints after pre-wipe." + fi +} +prewipe "$TARGET_DISK" + +if [[ $WITH_LUKS == true ]]; then + install -m 600 /dev/null "$LUKS_KEY_PATH" + trap 'rm -f "$LUKS_KEY_PATH" 2>/dev/null || true' EXIT + printf '%s' "$LUKS_PASSPHRASE" > "$LUKS_KEY_PATH" + unset LUKS_PASSPHRASE +fi + +disko_log=$(mktemp --suffix=.disko.log) +if ! disko --mode destroy,format,mount --yes-wipe-all-disks \ + --argstr mainDrive "$TARGET_DISK" \ + --arg withLuks "$WITH_LUKS" \ + --argstr swapSize "${SWAP_GB}G" \ + "$SHARE/disko-config.nix" >"$disko_log" 2>&1; then + tail -n 30 "$disko_log" + fail "disko failed — full log: $disko_log" +fi +rm -f "$LUKS_KEY_PATH" "$disko_log" +success "Disk partitioned and mounted at /mnt" + +# Hibernation plumbing: the swapfile's physical offset goes into the +# kernel cmdline. Deactivate swap first so nixos-generate-config doesn't +# also emit a swapDevices entry (we write our own, with resume wiring). +RESUME_CONFIG="" +if [[ "$SWAP_GB" != "0" ]]; then + swapoff -a 2>/dev/null || true + resume_offset=$(btrfs inspect-internal map-swapfile -r /mnt/swap/swapfile) + root_uuid=$(findmnt -no UUID /mnt) + RESUME_CONFIG=$(cat <<NIX + + # Swapfile (hibernation-ready: resume points into it). + swapDevices = [{ device = "/swap/swapfile"; }]; + boot.resumeDevice = "/dev/disk/by-uuid/$root_uuid"; + boot.kernelParams = [ "resume_offset=$resume_offset" ]; +NIX +) + success "Swapfile created (resume offset $resume_offset)" +fi + +# ─── Configuration generation ─────────────────────────────────────────── +section "Generating configuration" + +FLAKE_DIR="/mnt/home/$USERNAME/.nomarchy" +mkdir -p "$FLAKE_DIR" + +nixos-generate-config --root /mnt +mv /mnt/etc/nixos/hardware-configuration.nix "$FLAKE_DIR/" +rm -rf /mnt/etc/nixos + +cp "$SHARE/template/home.nix" "$FLAKE_DIR/" +cp "$SHARE/template/theme-state.json" "$FLAKE_DIR/" + +hw_nix="" +for p in "${HW_PROFILES[@]:-}"; do + [[ -n "$p" ]] && hw_nix+=" \"$p\"" +done + +cat > "$FLAKE_DIR/flake.nix" <<EOF +{ + description = "$HOSTNAME_ — my Nomarchy machine"; + + # The only input. nixpkgs, home-manager etc. come pinned through it — + # tested together upstream. Generated by nomarchy-install; your machine + # lives in system.nix and home.nix, this file is never hand-edited. + inputs.nomarchy.url = "${NOMARCHY_FLAKE_URL}"; + + outputs = { nomarchy, ... }: + nomarchy.lib.mkFlake { + src = ./.; + username = "$USERNAME"; + hardwareProfile = [$hw_nix ]; + }; +} +EOF + +AUTOLOGIN_CONFIG="" +if [[ $WITH_LUKS == true ]]; then + AUTOLOGIN_CONFIG=$(cat <<NIX + + # The LUKS passphrase already gates this machine — skip the second + # password prompt and boot straight into the desktop. + nomarchy.system.greeter.autoLogin = "$USERNAME"; +NIX +) +fi + +# initialHashedPassword is safe to template: mkpasswd's alphabet is +# [a-zA-Z0-9./$] — no Nix string metacharacters. +cat > "$FLAKE_DIR/system.nix" <<EOF +# Your machine: hostname, users, services. The distro itself comes from +# Nomarchy (via flake.nix); override its defaults here with plain NixOS +# options, or the nomarchy.system.* toggles. +{ pkgs, username, ... }: + +{ + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + networking.hostName = "$HOSTNAME_"; + time.timeZone = "$TIMEZONE"; + i18n.defaultLocale = "en_US.UTF-8"; + + # Your login user — \`username\` flows in from flake.nix automatically. + users.users.\${username} = { + isNormalUser = true; + extraGroups = [ "wheel" "networkmanager" "video" "input" ]; + initialHashedPassword = "$HASHED_PASSWORD"; + }; +$AUTOLOGIN_CONFIG$RESUME_CONFIG + # Hourly/daily BTRFS timeline snapshots + nixos-rebuild-snap. + nomarchy.system.snapper.enable = true; + + system.stateVersion = "26.05"; +} +EOF + +# The flake.lock: composed offline — nomarchy is path-locked to the very +# source the ISO carries (original stays the forge URL, so a later +# `nix flake update` on the installed machine re-resolves normally). +if ! python3 "$SHARE/compose-lock.py" "$SHARE/flake.lock" "$FLAKE_DIR/flake.lock" \ + "$NOMARCHY_LOCKED_JSON" "$NOMARCHY_ORIGINAL_JSON"; then + warn "Offline lock composition failed — resolving over the network." + (cd "$FLAKE_DIR" && nix --extra-experimental-features "nix-command flakes" flake lock) +fi + +# A flake worktree must be git-tracked (theme-state.json especially). +( + cd "$FLAKE_DIR" + git init -q + git add -A + git -c user.name="Nomarchy Installer" -c user.email="installer@nomarchy" \ + commit -qm "Initial Nomarchy configuration" +) + +# The user must own their flake — libgit2 refuses repositories owned by +# someone else, which breaks `home-manager switch` (and theme switching) +# outright. The first normal NixOS user is always 1000:users(100); the +# account doesn't exist in the target yet, so numeric ids it is. +chown -R 1000:100 "$FLAKE_DIR" + +# /etc/nixos on the installed system points at the user-owned flake. +mkdir -p /mnt/etc +ln -sfn "/home/$USERNAME/.nomarchy" /mnt/etc/nixos +success "Configuration written to ~$USERNAME/.nomarchy" + +# ─── Install ──────────────────────────────────────────────────────────── +section "Installing (this takes a while)" + +# Seed the target store with the flake source + all inputs so the first +# `nomarchy-theme-sync apply` (and the HM pre-activation below) work +# before the machine has ever seen a network. Two steps because +# `flake archive --to` enforces signatures and locally-evaluated source +# paths have none; plain `nix copy` accepts --no-check-sigs. +info "Seeding flake inputs into the target store..." +# path: (not git+file) — the flake dir is owned by the target user and +# root's libgit2 refuses repositories owned by someone else. +flake_paths=$(nix --extra-experimental-features "nix-command flakes" \ + flake archive --json "path:$FLAKE_DIR" \ + | python3 -c ' +import json, sys +def walk(node): + yield node["path"] + for child in node.get("inputs", {}).values(): + yield from walk(child) +print("\n".join(walk(json.load(sys.stdin))))' || true) +if [[ -n "$flake_paths" ]]; then + # shellcheck disable=SC2086 + nix --extra-experimental-features "nix-command flakes" \ + copy --no-check-sigs --to "local?root=/mnt" $flake_paths \ + || warn "input seeding failed — first rebuild will need network." +else + warn "flake archive failed — first rebuild will need network." +fi + +# Offline: sidestep substituter plumbing entirely — make every ISO store +# path valid in the target store up front. nixos-install's in-target +# build then finds all build tools locally and only the per-machine +# config derivations are built. (Two earlier attempts to route this +# through substituters — env config and the forwarded --substituters +# flag — still left the plan building gcc from source.) +if [[ ${#NIXOS_INSTALL_OPTS[@]} -gt 0 ]]; then + info "Copying the ISO store into the target (offline install)..." + nix --extra-experimental-features nix-command \ + copy --all --no-check-sigs --to "local?root=/mnt" +fi + +nixos-install --no-root-passwd "${NIXOS_INSTALL_OPTS[@]}" --flake "path:$FLAKE_DIR#default" +success "System installed (bootloader in place)" + +# Pre-activate the Home Manager generation so the FIRST boot lands in the +# fully themed desktop, not bare Hyprland. Best-effort: a failure here +# only costs the user one `home-manager switch` after logging in. +section "Baking the desktop" +# NOT /mnt/tmp: nixos-enter mounts a fresh tmpfs over /tmp inside the +# chroot, which silently vaporizes any script staged there (cost us a +# full verification round to find). /root persists into the chroot. +cat > /mnt/root/nomarchy-hm-activate.sh <<EOF +set -ex +exec > /var/log/nomarchy-hm-preactivate.log 2>&1 +export PATH=/run/current-system/sw/bin:\$PATH +out=\$(nix --extra-experimental-features "nix-command flakes" \ + build --no-link --print-out-paths \ + --option substituters "" \ + "path:/home/$USERNAME/.nomarchy#homeConfigurations.$USERNAME.activationPackage") +install -d -o "$USERNAME" -g users /nix/var/nix/profiles/per-user/$USERNAME +install -d -o "$USERNAME" -g users /nix/var/nix/gcroots/per-user/$USERNAME +# activate's profile ops need store access; as the user that means a +# daemon, and the chroot has none — run one for the duration. +nix-daemon & +daemon_pid=\$! +trap 'kill \$daemon_pid 2>/dev/null || true' EXIT +sleep 2 +# BACKUP_EXT: collisions can't abort the activation (a stray +# autogenerated config gets moved aside instead). +runuser -u "$USERNAME" -- bash -lc \ + "USER=$USERNAME HOME=/home/$USERNAME NIX_REMOTE=daemon HOME_MANAGER_BACKUP_EXT=bak \$out/activate" +EOF +if nixos-enter --root /mnt -- bash /root/nomarchy-hm-activate.sh; then + success "Desktop pre-activated — first boot is fully themed" +else + warn "Desktop pre-activation failed (see /var/log/nomarchy-hm-preactivate.log" + warn "on the installed system); after first login run:" + warn " home-manager switch --flake ~/.nomarchy -b bak" + tail -n 5 /mnt/var/log/nomarchy-hm-preactivate.log 2>/dev/null || true +fi +rm -f /mnt/root/nomarchy-hm-activate.sh + +header "Nomarchy installed on $TARGET_DISK" \ + "User: $USERNAME @ $HOSTNAME_" \ + "Remove the USB stick when the machine is off." + +finish="${NOMARCHY_FINISH:-ask}" +case "$finish" in + reboot) systemctl reboot ;; + poweroff) systemctl poweroff ;; + none) : ;; + *) confirm "Reboot into Nomarchy now?" && systemctl reboot || true ;; +esac diff --git a/pkgs/nomarchy-theme-sync/default.nix b/pkgs/nomarchy-theme-sync/default.nix new file mode 100644 index 0000000..35698c2 --- /dev/null +++ b/pkgs/nomarchy-theme-sync/default.nix @@ -0,0 +1,49 @@ +{ lib +, stdenvNoCC +, python3 +, makeWrapper +, awww +, libnotify +, git + # Shipped theme presets, baked into the package as a fallback so + # `list`/`apply` work even when $NOMARCHY_PATH has no themes/ dir. +, themesDir ? null +}: + +stdenvNoCC.mkDerivation { + pname = "nomarchy-theme-sync"; + version = "0.4.0"; + + src = ./.; + + nativeBuildInputs = [ makeWrapper ]; + buildInputs = [ python3 ]; + + installPhase = '' + runHook preInstall + + install -Dm755 nomarchy-theme-sync.py $out/bin/nomarchy-theme-sync + patchShebangs $out/bin/nomarchy-theme-sync + + ${lib.optionalString (themesDir != null) '' + mkdir -p $out/share/nomarchy + cp -r ${themesDir} $out/share/nomarchy/themes + ''} + + # Stdlib-only Python. home-manager is deliberately NOT wrapped in — + # the rebuild must use the user's own home-manager from their PATH. + wrapProgram $out/bin/nomarchy-theme-sync \ + --prefix PATH : ${lib.makeBinPath [ awww libnotify git ]} \ + ${lib.optionalString (themesDir != null) + "--set NOMARCHY_DEFAULT_THEMES $out/share/nomarchy/themes"} + + runHook postInstall + ''; + + meta = { + description = "Nomarchy theming: JSON state writer + Home Manager rebuild dispatcher"; + license = lib.licenses.mit; + mainProgram = "nomarchy-theme-sync"; + platforms = lib.platforms.linux; + }; +} diff --git a/pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py b/pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py new file mode 100644 index 0000000..ba136a7 --- /dev/null +++ b/pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python3 +"""nomarchy-theme-sync — state writer for Nomarchy's declarative theming. + +Single source of truth: $NOMARCHY_PATH/theme-state.json (inside the flake, +read purely by Home Manager via the nomarchy.stateFile option). + +Theme changes are applied by Home Manager: this tool writes the new state +and runs `home-manager switch` (override with $NOMARCHY_REBUILD, or skip +with --no-switch). All app theming — Hyprland, Waybar, Ghostty, btop, +Stylix — is baked into the generation; nothing is patched at runtime. + +The one runtime exception is the wallpaper: swww is imperative by nature, +so `bg next` cycles instantly and `wallpaper` (re-)applies the current one +at session start and after a switch. + +Commands: + list list theme presets + apply <name|file.json> merge a preset into the state + rebuild + set <dotted.path> <value> tweak one key (e.g. `set ui.gapsOut 16`) + rebuild + get [dotted.path] print the current state (or one key) + wallpaper apply the current wallpaper via swww + bg [next|auto] cycle the theme's wallpapers (instant, no rebuild) +""" + +import argparse +import json +import os +import shlex +import shutil +import subprocess +import sys +import tempfile +import time +from pathlib import Path + +# ─── Paths ──────────────────────────────────────────────────────────────── + +FLAKE_DIR = Path(os.environ.get("NOMARCHY_PATH", Path.home() / ".nomarchy")).expanduser() +STATE_FILE = FLAKE_DIR / "theme-state.json" + +# Preset search path: the user's flake first (their custom themes win), +# then the presets baked into this package by the Nix build. +THEMES_DIRS = [FLAKE_DIR / "themes"] +if os.environ.get("NOMARCHY_DEFAULT_THEMES"): + THEMES_DIRS.append(Path(os.environ["NOMARCHY_DEFAULT_THEMES"])) + +WALLPAPER_EXTS = {".png", ".jpg", ".jpeg", ".webp"} + +QUIET = False + + +def log(msg: str) -> None: + if not QUIET: + print(f"nomarchy-theme-sync: {msg}") + + +def die(msg: str) -> "None": + print(f"nomarchy-theme-sync: error: {msg}", file=sys.stderr) + sys.exit(1) + + +def notify(body: str) -> None: + if shutil.which("notify-send"): + subprocess.run(["notify-send", "-a", "Nomarchy", "Nomarchy", body], + capture_output=True) + + +# ─── State management ───────────────────────────────────────────────────── + +def load_state(path: Path = STATE_FILE) -> dict: + try: + return json.loads(path.read_text()) + except FileNotFoundError: + die(f"state file not found: {path} (set $NOMARCHY_PATH to your flake checkout)") + except json.JSONDecodeError as e: + die(f"invalid JSON in {path}: {e}") + + +def write_state(state: dict) -> None: + """Atomic write: render to a temp file in the same dir, then rename.""" + STATE_FILE.parent.mkdir(parents=True, exist_ok=True) + fd, tmp = tempfile.mkstemp(dir=STATE_FILE.parent, prefix=".theme-state.", suffix=".json") + try: + with os.fdopen(fd, "w") as f: + json.dump(state, f, indent=2) + f.write("\n") + os.replace(tmp, STATE_FILE) + except BaseException: + os.unlink(tmp) + raise + log(f"state written to {STATE_FILE}") + + # Flakes only see git-tracked files. theme-state.json ships tracked, + # but make sure a fresh checkout / reset can't silently hide it. + if (FLAKE_DIR / ".git").exists() and shutil.which("git"): + subprocess.run( + ["git", "-C", str(FLAKE_DIR), "add", "--intent-to-add", "theme-state.json"], + capture_output=True, + ) + + +def deep_merge(base: dict, override: dict) -> dict: + out = dict(base) + for k, v in override.items(): + if isinstance(v, dict) and isinstance(out.get(k), dict): + out[k] = deep_merge(out[k], v) + else: + out[k] = v + return out + + +# ─── Rebuild dispatch ───────────────────────────────────────────────────── + +def run_switch() -> None: + """Bake the new state into a Home Manager generation.""" + override = os.environ.get("NOMARCHY_REBUILD") + argv = shlex.split(override) if override else \ + ["home-manager", "switch", "--flake", str(FLAKE_DIR)] + + if shutil.which(argv[0]) is None: + log(f"'{argv[0]}' not found — state written; rebuild manually to apply") + return + + log(f"rebuilding: {' '.join(argv)}") + notify("Applying theme — rebuilding the desktop…") + result = subprocess.run(argv) # stream output to the caller's terminal + if result.returncode != 0: + notify("Theme rebuild FAILED — see terminal / journal") + die("rebuild failed (state file already updated; fix and re-run)") + notify("Theme applied ✓") + + +# ─── Theme assets (wallpapers) ──────────────────────────────────────────── +# Convention: a preset themes/<slug>.json may have a sibling directory +# themes/<slug>/ with assets: backgrounds/ (wallpapers), btop.theme, +# waybar.css / waybar.jsonc. All except backgrounds/ are consumed by the +# Nix modules at eval time; wallpapers are applied here via swww. + +def find_asset(slug: str, name: str): + for themes_dir in THEMES_DIRS: + candidate = themes_dir / slug / name + if candidate.exists(): + return candidate + return None + + +def backgrounds_for(slug: str) -> list: + bg_dir = find_asset(slug, "backgrounds") + if bg_dir is None or not bg_dir.is_dir(): + return [] + return sorted(p for p in bg_dir.iterdir() if p.suffix.lower() in WALLPAPER_EXTS) + + +def resolve_wallpaper(state: dict): + """Explicit path in the state wins; empty means 'first theme background'.""" + explicit = state.get("wallpaper", "") + if explicit: + path = Path(explicit).expanduser() + if path.is_file(): + return path + log(f"wallpaper not found, using theme default: {explicit}") + backgrounds = backgrounds_for(state.get("slug", "")) + return backgrounds[0] if backgrounds else None + + +def apply_wallpaper(state: dict, wait: bool = False) -> None: + wallpaper = resolve_wallpaper(state) + if wallpaper is None: + log(f"no wallpaper for theme '{state.get('slug', '?')}', skipping") + return + # nixpkgs renamed swww to awww (CLI-compatible fork); accept either. + swww = shutil.which("awww") or shutil.which("swww") + if swww is None: + log("awww/swww not found, skipping wallpaper") + return + # At session start the daemon may still be coming up. + for _ in range(10 if wait else 1): + if subprocess.run([swww, "query"], capture_output=True).returncode == 0: + break + time.sleep(0.5) + result = subprocess.run( + [swww, "img", str(wallpaper), + "--transition-type", "grow", + "--transition-pos", "center", + "--transition-duration", "1", + "--transition-fps", "60"], + capture_output=True, + ) + log(f"wallpaper: {wallpaper.name}" if result.returncode == 0 + else "wallpaper: awww failed (daemon not running?)") + + +# ─── Commands ───────────────────────────────────────────────────────────── + +def find_preset(name: str): + for themes_dir in THEMES_DIRS: + candidate = themes_dir / f"{name}.json" + if candidate.is_file(): + return candidate + return None + + +def cmd_list(_args) -> None: + names = sorted({f.stem for d in THEMES_DIRS if d.is_dir() for f in d.glob("*.json")}) + if not names: + die(f"no theme presets found (searched: {', '.join(map(str, THEMES_DIRS))})") + print("\n".join(names)) + + +def cmd_apply(args) -> None: + candidate = Path(args.theme).expanduser() + preset_path = candidate if candidate.is_file() else find_preset(args.theme) + if preset_path is None: + die(f"unknown theme '{args.theme}' (try `nomarchy-theme-sync list`)") + + preset = json.loads(preset_path.read_text()) + # Merge over current state: presets define palette/name/wallpaper, + # user tweaks (gaps, fonts) outside the preset survive. + state = deep_merge(load_state(), preset) + write_state(state) + log(f"theme: {state.get('name', args.theme)}") + if not args.no_switch: + run_switch() + apply_wallpaper(state) + + +def cmd_set(args) -> None: + state = load_state() + try: + value = json.loads(args.value) # numbers, bools, null, quoted strings + except json.JSONDecodeError: + value = args.value # bare string ("#7aa2f7", font names, paths) + + node = state + keys = args.path.split(".") + for key in keys[:-1]: + node = node.setdefault(key, {}) + if not isinstance(node, dict): + die(f"cannot descend into non-object at '{key}' in '{args.path}'") + node[keys[-1]] = value + + write_state(state) + log(f"set {args.path} = {value!r}") + if not args.no_switch: + run_switch() + apply_wallpaper(state) + + +def cmd_get(args) -> None: + state = load_state() + if args.path: + node = state + for key in args.path.split("."): + try: + node = node[key] + except (KeyError, TypeError): + die(f"no such key: {args.path}") + print(json.dumps(node, indent=2) if isinstance(node, (dict, list)) else node) + else: + print(json.dumps(state, indent=2)) + + +def cmd_wallpaper(_args) -> None: + """Apply the current wallpaper (session start, post-switch hook).""" + apply_wallpaper(load_state(), wait=True) + + +def cmd_bg(args) -> None: + """Cycle the theme's wallpapers — instant, no rebuild needed (the + wallpaper is runtime state for swww; nothing in Nix consumes it).""" + state = load_state() + if args.action == "auto": + state["wallpaper"] = "" + else: # next + backgrounds = backgrounds_for(state.get("slug", "")) + if not backgrounds: + die(f"theme '{state.get('slug', '?')}' has no backgrounds/ directory") + current = resolve_wallpaper(state) + idx = (backgrounds.index(current) + 1) % len(backgrounds) if current in backgrounds else 0 + state["wallpaper"] = str(backgrounds[idx]) + write_state(state) + apply_wallpaper(state) + + +# ─── Entry point ────────────────────────────────────────────────────────── + +def main() -> None: + global QUIET + + parser = argparse.ArgumentParser( + prog="nomarchy-theme-sync", + description="Nomarchy theming — state writer + Home Manager rebuild dispatcher.", + ) + parser.add_argument("--quiet", action="store_true", help="suppress progress output") + sub = parser.add_subparsers(dest="command", required=True) + + sub.add_parser("list", help="list theme presets").set_defaults(func=cmd_list) + + p = sub.add_parser("apply", help="apply a theme preset (name or JSON file) + rebuild") + p.add_argument("theme") + p.add_argument("--no-switch", action="store_true", help="write state only, skip the rebuild") + p.set_defaults(func=cmd_apply) + + p = sub.add_parser("set", help="set one key, e.g. `set ui.gapsOut 16` + rebuild") + p.add_argument("path") + p.add_argument("value") + p.add_argument("--no-switch", action="store_true", help="write state only, skip the rebuild") + p.set_defaults(func=cmd_set) + + p = sub.add_parser("get", help="print state (or one dotted key)") + p.add_argument("path", nargs="?") + p.set_defaults(func=cmd_get) + + sub.add_parser("wallpaper", help="apply the current wallpaper via swww").set_defaults(func=cmd_wallpaper) + + p = sub.add_parser("bg", help="wallpaper control: `bg next` cycles, `bg auto` resets") + p.add_argument("action", choices=["next", "auto"], default="next", nargs="?") + p.set_defaults(func=cmd_bg) + + args = parser.parse_args() + QUIET = args.quiet + try: + args.func(args) + except KeyboardInterrupt: + sys.exit(130) + + +if __name__ == "__main__": + main() diff --git a/templates/downstream/README.md b/templates/downstream/README.md new file mode 100644 index 0000000..a791685 --- /dev/null +++ b/templates/downstream/README.md @@ -0,0 +1,27 @@ +# My Nomarchy machine + +1. Overwrite the placeholder hardware config with the real one: + `nixos-generate-config --show-hardware-config > hardware-configuration.nix` +2. Set `flake.nix` up **once** (Nomarchy repo URL, your username, optionally + a `hardwareProfile` from <https://github.com/NixOS/nixos-hardware> — an + unknown name fails the rebuild with suggestions). After that the flake is + never touched again: your machine lives in `system.nix` (hostname, + services — the login user is created from `username` automatically) and + `home.nix` (your packages). +3. `git init && git add -A` — flakes only see tracked files, including + `theme-state.json`. +4. System: `sudo nixos-rebuild switch --flake .#default` +5. Desktop: `nix run home-manager -- switch --flake .#me` + (afterwards just `home-manager switch --flake .#me` — it's installed) +6. Clone/symlink this directory to `~/.nomarchy` (or export + `NOMARCHY_PATH`) so `nomarchy-theme-sync` knows where the state lives. + +Day-to-day: + +```sh +nomarchy-theme-sync list # 21 shipped presets +nomarchy-theme-sync apply gruvbox # writes state + home-manager switch +nomarchy-theme-sync bg next # cycle wallpapers (instant, no rebuild) +``` + +The system layer only needs `nixos-rebuild` when you change `system.nix`. diff --git a/templates/downstream/flake.nix b/templates/downstream/flake.nix new file mode 100644 index 0000000..a764067 --- /dev/null +++ b/templates/downstream/flake.nix @@ -0,0 +1,20 @@ +{ + description = "My Nomarchy machine"; + + # The only input. nixpkgs, home-manager etc. come pinned through it — + # tested together upstream. This file is written once (by you or the + # installer) and never hand-edited afterwards; your machine lives in + # system.nix and home.nix. v1 is the release branch. + inputs.nomarchy.url = "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1"; + + outputs = { nomarchy, ... }: + nomarchy.lib.mkFlake { + src = ./.; + username = "me"; # <- your login name + + # Optional: a nixos-hardware module name for your machine, e.g. + # hardwareProfile = "framework-13-7040-amd"; + # Names: https://github.com/NixOS/nixos-hardware + # (the future installer fills this in automatically from DMI data) + }; +} diff --git a/templates/downstream/hardware-configuration.nix b/templates/downstream/hardware-configuration.nix new file mode 100644 index 0000000..5691209 --- /dev/null +++ b/templates/downstream/hardware-configuration.nix @@ -0,0 +1,15 @@ +# PLACEHOLDER — replace with the output of `nixos-generate-config` on the +# target machine. The mkDefault values below only exist so the flake +# evaluates and `nix flake check` passes before first install. +{ lib, ... }: + +{ + boot.initrd.availableKernelModules = lib.mkDefault [ "xhci_pci" "ahci" "nvme" "usbhid" "sd_mod" ]; + + fileSystems."/" = lib.mkDefault { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/templates/downstream/home.nix b/templates/downstream/home.nix new file mode 100644 index 0000000..29b4ba1 --- /dev/null +++ b/templates/downstream/home.nix @@ -0,0 +1,16 @@ +# Your user environment. The Nomarchy desktop (Hyprland, Waybar, +# Ghostty, theming engine, Stylix) comes from homeModules.nomarchy; +# tune it via the nomarchy.* options, add your own packages and +# programs below. +{ pkgs, ... }: + +{ + # Examples: + # nomarchy.terminal = "kitty"; # swap the default terminal + # nomarchy.waybar.enable = false; # bring your own bar + # nomarchy.stylix.enable = false; # opt out of GTK/Qt theming + + home.packages = with pkgs; [ + # firefox + ]; +} diff --git a/templates/downstream/system.nix b/templates/downstream/system.nix new file mode 100644 index 0000000..9c2c1e4 --- /dev/null +++ b/templates/downstream/system.nix @@ -0,0 +1,26 @@ +# Your machine: bootloader, hostname, users, services. +# The distro itself comes from nomarchy.nixosModules.nomarchy — override +# any of its defaults here with plain NixOS options (they use mkDefault), +# or via the nomarchy.system.* toggles. +{ pkgs, username, ... }: + +{ + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + networking.hostName = "my-nomarchy"; + time.timeZone = "UTC"; + i18n.defaultLocale = "en_US.UTF-8"; + + # Your login user — `username` flows in from flake.nix automatically. + users.users.${username} = { + isNormalUser = true; + extraGroups = [ "wheel" "networkmanager" "video" "input" ]; + }; + + # Examples: + # nomarchy.system.greeter.enable = false; # bring your own login manager + # environment.systemPackages = [ pkgs.htop ]; + + system.stateVersion = "26.05"; +} diff --git a/templates/downstream/theme-state.json b/templates/downstream/theme-state.json new file mode 100644 index 0000000..635e11d --- /dev/null +++ b/templates/downstream/theme-state.json @@ -0,0 +1,55 @@ +{ + "version": 1, + "name": "Tokyo Night", + "slug": "tokyo-night", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#1a1b26", + "mantle": "#161720", + "surface": "#32344a", + "overlay": "#444b6a", + "text": "#a9b1d6", + "subtext": "#787c99", + "muted": "#444b6a", + "accent": "#7aa2f7", + "accentAlt": "#ad8ee6", + "good": "#9ece6a", + "warn": "#e0af68", + "bad": "#f7768e" + }, + "ansi": [ + "#32344a", + "#f7768e", + "#9ece6a", + "#e0af68", + "#7aa2f7", + "#ad8ee6", + "#449dab", + "#787c99", + "#444b6a", + "#ff7a93", + "#b9f27c", + "#ff9e64", + "#7da6ff", + "#bb9af7", + "#0db9d7", + "#acb0d0" + ], + "fonts": { + "mono": "JetBrainsMono Nerd Font", + "ui": "Inter", + "size": 11 + }, + "ui": { + "gapsIn": 5, + "gapsOut": 12, + "borderSize": 2, + "rounding": 10, + "activeOpacity": 1.0, + "inactiveOpacity": 0.95, + "terminalOpacity": 0.96, + "blur": true, + "shadow": true + } +} diff --git a/theme-state.json b/theme-state.json new file mode 100644 index 0000000..635e11d --- /dev/null +++ b/theme-state.json @@ -0,0 +1,55 @@ +{ + "version": 1, + "name": "Tokyo Night", + "slug": "tokyo-night", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#1a1b26", + "mantle": "#161720", + "surface": "#32344a", + "overlay": "#444b6a", + "text": "#a9b1d6", + "subtext": "#787c99", + "muted": "#444b6a", + "accent": "#7aa2f7", + "accentAlt": "#ad8ee6", + "good": "#9ece6a", + "warn": "#e0af68", + "bad": "#f7768e" + }, + "ansi": [ + "#32344a", + "#f7768e", + "#9ece6a", + "#e0af68", + "#7aa2f7", + "#ad8ee6", + "#449dab", + "#787c99", + "#444b6a", + "#ff7a93", + "#b9f27c", + "#ff9e64", + "#7da6ff", + "#bb9af7", + "#0db9d7", + "#acb0d0" + ], + "fonts": { + "mono": "JetBrainsMono Nerd Font", + "ui": "Inter", + "size": 11 + }, + "ui": { + "gapsIn": 5, + "gapsOut": 12, + "borderSize": 2, + "rounding": 10, + "activeOpacity": 1.0, + "inactiveOpacity": 0.95, + "terminalOpacity": 0.96, + "blur": true, + "shadow": true + } +} diff --git a/themes/catppuccin-latte.json b/themes/catppuccin-latte.json new file mode 100644 index 0000000..a6565d7 --- /dev/null +++ b/themes/catppuccin-latte.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Catppuccin Latte", + "slug": "catppuccin-latte", + "mode": "light", + "wallpaper": "", + "colors": { + "base": "#eff1f5", + "mantle": "#cbcdd0", + "surface": "#bcc0cc", + "overlay": "#acb0be", + "text": "#4c4f69", + "subtext": "#5c5f77", + "muted": "#acb0be", + "accent": "#1e66f5", + "accentAlt": "#ea76cb", + "good": "#40a02b", + "warn": "#df8e1d", + "bad": "#d20f39" + }, + "ansi": [ + "#bcc0cc", + "#d20f39", + "#40a02b", + "#df8e1d", + "#1e66f5", + "#ea76cb", + "#179299", + "#5c5f77", + "#acb0be", + "#d20f39", + "#40a02b", + "#df8e1d", + "#1e66f5", + "#ea76cb", + "#179299", + "#6c6f85" + ] +} diff --git a/themes/palettes/catppuccin-latte/backgrounds/1-color-fade.png b/themes/catppuccin-latte/backgrounds/1-color-fade.png similarity index 100% rename from themes/palettes/catppuccin-latte/backgrounds/1-color-fade.png rename to themes/catppuccin-latte/backgrounds/1-color-fade.png diff --git a/themes/palettes/catppuccin-latte/apps/btop.theme b/themes/catppuccin-latte/btop.theme similarity index 100% rename from themes/palettes/catppuccin-latte/apps/btop.theme rename to themes/catppuccin-latte/btop.theme diff --git a/themes/catppuccin.json b/themes/catppuccin.json new file mode 100644 index 0000000..da34057 --- /dev/null +++ b/themes/catppuccin.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Catppuccin", + "slug": "catppuccin", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#1e1e2e", + "mantle": "#1a1a27", + "surface": "#45475a", + "overlay": "#585b70", + "text": "#cdd6f4", + "subtext": "#bac2de", + "muted": "#585b70", + "accent": "#89b4fa", + "accentAlt": "#f5c2e7", + "good": "#a6e3a1", + "warn": "#f9e2af", + "bad": "#f38ba8" + }, + "ansi": [ + "#45475a", + "#f38ba8", + "#a6e3a1", + "#f9e2af", + "#89b4fa", + "#f5c2e7", + "#94e2d5", + "#bac2de", + "#585b70", + "#f38ba8", + "#a6e3a1", + "#f9e2af", + "#89b4fa", + "#f5c2e7", + "#94e2d5", + "#a6adc8" + ] +} diff --git a/themes/palettes/catppuccin/backgrounds/1-totoro.png b/themes/catppuccin/backgrounds/1-totoro.png similarity index 100% rename from themes/palettes/catppuccin/backgrounds/1-totoro.png rename to themes/catppuccin/backgrounds/1-totoro.png diff --git a/themes/palettes/catppuccin/backgrounds/2-waves.png b/themes/catppuccin/backgrounds/2-waves.png similarity index 100% rename from themes/palettes/catppuccin/backgrounds/2-waves.png rename to themes/catppuccin/backgrounds/2-waves.png diff --git a/themes/palettes/catppuccin/backgrounds/3-blue-eye.png b/themes/catppuccin/backgrounds/3-blue-eye.png similarity index 100% rename from themes/palettes/catppuccin/backgrounds/3-blue-eye.png rename to themes/catppuccin/backgrounds/3-blue-eye.png diff --git a/themes/palettes/catppuccin/apps/btop.theme b/themes/catppuccin/btop.theme similarity index 100% rename from themes/palettes/catppuccin/apps/btop.theme rename to themes/catppuccin/btop.theme diff --git a/themes/catppuccin/waybar.css b/themes/catppuccin/waybar.css new file mode 100644 index 0000000..bf35a40 --- /dev/null +++ b/themes/catppuccin/waybar.css @@ -0,0 +1,2 @@ +@define-color foreground #cdd6f4; +@define-color background #181824; diff --git a/themes/engine/files.nix b/themes/engine/files.nix deleted file mode 100644 index 42ecddb..0000000 --- a/themes/engine/files.nix +++ /dev/null @@ -1,173 +0,0 @@ -{ config, lib, ... }: - -let - nomarchyLib = import ../../lib { inherit lib; }; - themePath = ../palettes + "/${config.nomarchy.theme}"; - themeAppsPath = themePath + "/apps"; - nordAppsPath = ../../features/desktop/hyprland/themes/nord; - hyprlandThemePath = ../../features/desktop/hyprland/themes + "/${config.nomarchy.theme}"; - - # Check if theme has hyprland.conf in palette apps/ or feature themes/ - hasHyprlandConf = builtins.pathExists (themeAppsPath + "/hyprland.conf"); - hasHyprlandFeatureConf = builtins.pathExists (hyprlandThemePath + "/hyprland.conf"); - - hyprlandConfSource = - if hasHyprlandConf then themeAppsPath + "/hyprland.conf" - else if hasHyprlandFeatureConf then hyprlandThemePath + "/hyprland.conf" - else nordAppsPath + "/hyprland.conf"; - - # Get palette for dynamic CSS generation - palette = nomarchyLib.getPalette config.nomarchy.theme; -in -{ - xdg.configFile."nomarchy/current/theme" = { - source = themePath; - recursive = true; - }; - - # Generate waybar.css if it doesn't exist in the theme - # This provides the @background and @foreground variables used by the default style - xdg.configFile."nomarchy/current/theme/waybar.css".text = '' - @define-color background #${palette.base00}; - @define-color foreground #${palette.base05}; - @define-color accent #${palette.base0D}; - ''; - - # Per-palette kitty colors. features/apps/kitty/config/kitty.conf includes - # this file; without it the include silently failed and kitty stayed on - # default colors for every palette (Stylix's kitty target only kicks in - # when programs.kitty.enable is set, which the module doesn't use). - xdg.configFile."nomarchy/current/theme/kitty.conf".text = '' - background #${palette.base00} - foreground #${palette.base05} - cursor #${palette.base05} - selection_background #${palette.base02} - selection_foreground #${palette.base05} - color0 #${palette.base01} - color1 #${palette.base08} - color2 #${palette.base0B} - color3 #${palette.base0A} - color4 #${palette.base0D} - color5 #${palette.base0E} - color6 #${palette.base0C} - color7 #${palette.base04} - color8 #${palette.base03} - color9 #${palette.base08} - color10 #${palette.base0B} - color11 #${palette.base0A} - color12 #${palette.base0D} - color13 #${palette.base0E} - color14 #${palette.base0C} - color15 #${palette.base07} - ''; - - # Per-palette ghostty colors. features/apps/ghostty/config/config uses an - # optional include (?-prefix) of this file; without it the include was - # silently skipped and ghostty rendered with its built-in defaults across - # every palette. ghostty has no Stylix target. - xdg.configFile."nomarchy/current/theme/ghostty.conf".text = '' - background = ${palette.base00} - foreground = ${palette.base05} - cursor-color = ${palette.base05} - selection-background = ${palette.base02} - selection-foreground = ${palette.base05} - palette = 0=#${palette.base01} - palette = 1=#${palette.base08} - palette = 2=#${palette.base0B} - palette = 3=#${palette.base0A} - palette = 4=#${palette.base0D} - palette = 5=#${palette.base0E} - palette = 6=#${palette.base0C} - palette = 7=#${palette.base04} - palette = 8=#${palette.base03} - palette = 9=#${palette.base08} - palette = 10=#${palette.base0B} - palette = 11=#${palette.base0A} - palette = 12=#${palette.base0D} - palette = 13=#${palette.base0E} - palette = 14=#${palette.base0C} - palette = 15=#${palette.base07} - ''; - - # Ensure theme-specific hyprland config exists - # Lookup priority: palette apps/ > feature themes/ > nord fallback - xdg.configFile."nomarchy/current/theme/apps/hyprland.conf" = lib.mkIf (!hasHyprlandConf) { - source = if hasHyprlandFeatureConf then hyprlandThemePath + "/hyprland.conf" - else nordAppsPath + "/hyprland.conf"; - }; - - # Legacy compatibility: symlink apps/hyprland.conf to root for scripts expecting old path - xdg.configFile."nomarchy/current/theme/hyprland.conf" = { - source = hyprlandConfSource; - }; - - # Rofi fallback support - xdg.configFile."rofi/config.rasi" = lib.mkIf (builtins.pathExists (themeAppsPath + "/rofi.rasi")) { - source = themeAppsPath + "/rofi.rasi"; - }; - - # Dynamically generated CSS for Walker app launcher - xdg.configFile."nomarchy/current/theme/apps/walker/style.css".text = '' - #window { - background-color: #${palette.base00}; - 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; - - # Expose branding assets - xdg.configFile."nomarchy/branding/logo.png".source = ../../core/branding/logo.png; - xdg.configFile."nomarchy/branding/logo.txt".source = ../../core/branding/logo.txt; - xdg.configFile."nomarchy/branding/logo.svg".source = ../../core/branding/logo.svg; - xdg.configFile."nomarchy/branding/icon.png".source = ../../core/branding/icon.png; - xdg.configFile."nomarchy/branding/icon.txt".source = ../../core/branding/icon.txt; - xdg.configFile."nomarchy/branding/screensaver.txt".source = ../../core/branding/screensaver.txt; - xdg.configFile."nomarchy/branding/about.txt".source = ../../core/branding/about.txt; - - # Expose all themes to the system via local share for script accessibility - xdg.dataFile."nomarchy/themes".source = builtins.path { - name = "nomarchy-themes"; - path = ../palettes; - }; - - # Expose all theme templates to the system via local share - xdg.dataFile."nomarchy/templates".source = builtins.path { - name = "nomarchy-templates"; - path = ../templates; - }; - - # Nautilus python extensions - xdg.dataFile."nautilus-python/extensions/localsend.py".source = ../../core/home/config/nautilus-python/extensions/localsend.py; -} diff --git a/themes/engine/loader.nix b/themes/engine/loader.nix deleted file mode 100644 index efbc8f4..0000000 --- a/themes/engine/loader.nix +++ /dev/null @@ -1,113 +0,0 @@ -{ config, lib, pkgs, ... }: - -# Theme Loader Module -# -# This module handles loading and deploying theme-specific application configs. -# It reads the active theme from state and deploys configs from the theme's apps/ -# subdirectory to appropriate locations. -# -# Theme structure expected: -# assets/themes/<theme-name>/ -# ├── colors.toml # Color palette (required) -# ├── light.mode # Marker for light themes (optional) -# ├── icons.theme # Icon theme name -# ├── backgrounds/ # Wallpapers -# ├── apps/ # App-specific themed configs -# │ ├── alacritty.toml -# │ ├── kitty.conf -# │ ├── waybar.css -# │ ├── hyprland-colors.conf -# │ ├── mako.conf -# │ ├── swayosd.css -# │ ├── btop.theme -# │ ├── vscode.json -# │ └── neovim.lua -# └── preview.png - -let - nomarchyLib = import ../../lib { inherit lib; }; - assetsPath = ../palettes; - activeTheme = config.nomarchy.theme; - themePath = assetsPath + "/${activeTheme}"; - themeAppsPath = themePath + "/apps"; - - # Check if a theme has an apps directory - themeHasApps = builtins.pathExists themeAppsPath; - - # Get the color palette for template processing - palette = nomarchyLib.getPalette activeTheme; - - # Helper to check if a file exists in theme - hasThemeFile = name: builtins.pathExists (themePath + "/${name}"); - hasThemeAppFile = name: themeHasApps && builtins.pathExists (themeAppsPath + "/${name}"); - - # All app configs are now in apps/ subdirectory - # Legacy root-level files are no longer supported - -in -{ - options.nomarchy.themeLoader = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to enable automatic theme app config loading."; - }; - - apps = { - # waybar, kitty, alacritty, and mako are intentionally absent. Waybar - # themes inline in features/desktop/waybar via colorScheme; kitty and - # alacritty are themed by stylix targets (themes/engine/stylix.nix); mako - # has no theme integration yet. Only btop is loaded from the active - # theme's apps/ directory. - btop = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to load btop theme from active theme."; - }; - }; - }; - - config = lib.mkIf config.nomarchy.themeLoader.enable { - # Deploy btop theme if available in apps/ - xdg.configFile."btop/themes/nomarchy.theme" = lib.mkIf (config.nomarchy.themeLoader.apps.btop && hasThemeAppFile "btop.theme") { - source = lib.mkDefault (themeAppsPath + "/btop.theme"); - }; - - # Note: Waybar CSS is generated inline in waybar.nix using colorScheme - # This loader would be used if themes provide complete waybar.css overrides - # For now, theme waybar.css files are only used for themes that need - # significantly different styling (like retro-82) - - # Create theme info file for scripts - xdg.configFile."nomarchy/theme-loader/current".text = '' - THEME_NAME="${activeTheme}" - THEME_PATH="${toString themePath}" - THEME_HAS_APPS="${if themeHasApps then "true" else "false"}" - IS_LIGHT_MODE="${if config.nomarchy.isLightMode then "true" else "false"}" - ICONS_THEME="${config.nomarchy.iconsTheme}" - ''; - - # Expose palette as shell-sourceable file for scripts - xdg.configFile."nomarchy/theme-loader/palette.sh".text = '' - # Auto-generated palette for ${activeTheme} - # Source this file in scripts to get theme colors - - BASE00="${palette.base00}" - BASE01="${palette.base01}" - BASE02="${palette.base02}" - BASE03="${palette.base03}" - BASE04="${palette.base04}" - BASE05="${palette.base05}" - BASE06="${palette.base06}" - BASE07="${palette.base07}" - BASE08="${palette.base08}" - BASE09="${palette.base09}" - BASE0A="${palette.base0A}" - BASE0B="${palette.base0B}" - BASE0C="${palette.base0C}" - BASE0D="${palette.base0D}" - BASE0E="${palette.base0E}" - BASE0F="${palette.base0F}" - ''; - }; -} diff --git a/themes/engine/plymouth.nix b/themes/engine/plymouth.nix deleted file mode 100644 index 12e27ab..0000000 --- a/themes/engine/plymouth.nix +++ /dev/null @@ -1,77 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - nomarchyLib = import ../../lib { inherit lib; }; - palette = nomarchyLib.getPalette config.nomarchy.system.theme; - - # Plymouth's Window.SetBackgroundTopColor takes three floats in 0.0–1.0; - # the .plymouth metadata's ConsoleLogBackgroundColor takes a 0xRRGGBB - # hex. Compute both from palette.base00 so the boot splash matches the - # rest of the active theme instead of staying frozen on the hardcoded - # Tokyo-Night-ish #1a1b26 it used to ship with. - hex = palette.base00; - rByte = lib.fromHexString (lib.substring 0 2 hex); - gByte = lib.fromHexString (lib.substring 2 2 hex); - bByte = lib.fromHexString (lib.substring 4 2 hex); - - # byte → "0.XXX" decimal string. Nix has no floating-point math, so - # multiply first, integer-divide, then format. Plymouth tolerates more - # decimals than this (0.1019 etc.) but three digits matches the visual - # precision Plymouth renders at and keeps the substitution readable. - byteToFloat = n: - let - thousandths = (n * 1000) / 255; - s = toString thousandths; - padded = - if lib.stringLength s == 1 then "00${s}" - else if lib.stringLength s == 2 then "0${s}" - else s; - in "0.${padded}"; - - bgR = byteToFloat rByte; - bgG = byteToFloat gByte; - bgB = byteToFloat bByte; - - nomarchy-plymouth = pkgs.stdenv.mkDerivation { - pname = "nomarchy-plymouth"; - version = "1.0"; - - src = ./plymouth; - - installPhase = '' - mkdir -p $out/share/plymouth/themes/nomarchy - cp * $out/share/plymouth/themes/nomarchy/ - - # Fix path in the plymouth file to point to the nix store - sed -i "s|/[a-z]*/share/plymouth/themes/nomarchy|$out/share/plymouth/themes/nomarchy|g" \ - $out/share/plymouth/themes/nomarchy/nomarchy.plymouth - - # Substitute the active theme's base00 in both the .script (RGB - # floats for the Window.SetBackground* calls) and the .plymouth - # metadata (0xRRGGBB for ConsoleLogBackgroundColor). - sed -i \ - -e 's|@BG_R@|${bgR}|g' \ - -e 's|@BG_G@|${bgG}|g' \ - -e 's|@BG_B@|${bgB}|g' \ - $out/share/plymouth/themes/nomarchy/nomarchy.script - sed -i 's|@BG_HEX@|${hex}|g' \ - $out/share/plymouth/themes/nomarchy/nomarchy.plymouth - ''; - }; -in -{ - boot.initrd.systemd.enable = lib.mkDefault true; - boot.initrd.verbose = lib.mkDefault false; - console.earlySetup = lib.mkDefault true; - boot.consoleLogLevel = lib.mkDefault 0; - boot.plymouth = { - enable = lib.mkDefault true; - themePackages = lib.mkDefault [ nomarchy-plymouth ]; - theme = lib.mkDefault "nomarchy"; - }; - - # Not mkDefault: kernelParams is a list nixpkgs and other modules add to at - # normal priority, so a mkDefault def is dropped — losing the quiet/splash - # boot. These params merge with (don't replace) the rest. - boot.kernelParams = [ "quiet" "splash" "loglevel=3" "rd.systemd.show_status=false" "rd.udev.log_level=3" "udev.log_priority=3" "boot.shell_on_fail" ]; -} diff --git a/themes/engine/plymouth/bullet.png b/themes/engine/plymouth/bullet.png deleted file mode 100644 index 62249b3..0000000 Binary files a/themes/engine/plymouth/bullet.png and /dev/null differ diff --git a/themes/engine/plymouth/entry.png b/themes/engine/plymouth/entry.png deleted file mode 100644 index 5c78917..0000000 Binary files a/themes/engine/plymouth/entry.png and /dev/null differ diff --git a/themes/engine/plymouth/lock.png b/themes/engine/plymouth/lock.png deleted file mode 100644 index 3046de1..0000000 Binary files a/themes/engine/plymouth/lock.png and /dev/null differ diff --git a/themes/engine/plymouth/logo.png b/themes/engine/plymouth/logo.png deleted file mode 100644 index dbcde56..0000000 Binary files a/themes/engine/plymouth/logo.png and /dev/null differ diff --git a/themes/engine/plymouth/nomarchy.plymouth b/themes/engine/plymouth/nomarchy.plymouth deleted file mode 100644 index 4ade91c..0000000 --- a/themes/engine/plymouth/nomarchy.plymouth +++ /dev/null @@ -1,11 +0,0 @@ -[Plymouth Theme] -Name=Nomarchy -Description=Nomarchy splash screen. -ModuleName=script - -[script] -ImageDir=/usr/share/plymouth/themes/nomarchy -ScriptFile=/usr/share/plymouth/themes/nomarchy/nomarchy.script -ConsoleLogBackgroundColor=0x@BG_HEX@ -MonospaceFont=Cantarell 11 -Font=Cantarell 11 diff --git a/themes/engine/plymouth/nomarchy.script b/themes/engine/plymouth/nomarchy.script deleted file mode 100644 index 216dac9..0000000 --- a/themes/engine/plymouth/nomarchy.script +++ /dev/null @@ -1,271 +0,0 @@ -# Nomarchy Plymouth Theme Script - -Window.SetBackgroundTopColor(@BG_R@, @BG_G@, @BG_B@); -Window.SetBackgroundBottomColor(@BG_R@, @BG_G@, @BG_B@); - -logo.image = Image("logo.png"); - -# Calculate scale factor to make logo ~15% of screen height -logo_scale_factor = (Window.GetHeight() * 0.15) / logo.image.GetHeight(); -logo_width = logo.image.GetWidth() * logo_scale_factor; -logo_height = logo.image.GetHeight() * logo_scale_factor; -logo.image = logo.image.Scale(logo_width, logo_height); - -logo.sprite = Sprite(logo.image); -logo.sprite.SetX (Window.GetWidth() / 2 - logo.image.GetWidth() / 2); -logo.sprite.SetY (Window.GetHeight() / 2 - logo.image.GetHeight() / 2); -logo.sprite.SetOpacity (1); - -# Use these to adjust the progress bar timing -global.fake_progress_limit = 0.7; # Target percentage for fake progress (0.0 to 1.0) -global.fake_progress_duration = 15.0; # Duration in seconds to reach limit - -# Progress bar animation variables -global.fake_progress = 0.0; -global.real_progress = 0.0; -global.fake_progress_active = 0; # 0 / 1 boolean -global.animation_frame = 0; -global.fake_progress_start_time = 0; # Track when fake progress started -global.password_shown = 0; # Track if password dialog has been shown -global.max_progress = 0.0; # Track the maximum progress reached to prevent backwards movement - -fun refresh_callback () - { - global.animation_frame++; - - # Animate fake progress to limit over time with easing - if (global.fake_progress_active == 1) - { - # Calculate elapsed time since start - elapsed_time = global.animation_frame / 50.0; # Convert frames to seconds (50 FPS) - - # Calculate linear progress ratio (0 to 1) based on time - time_ratio = elapsed_time / global.fake_progress_duration; - if (time_ratio > 1.0) - time_ratio = 1.0; - - # Apply easing curve: ease-out quadratic - # Formula: 1 - (1 - x)^2 - eased_ratio = 1 - ((1 - time_ratio) * (1 - time_ratio)); - - # Calculate fake progress based on eased ratio - global.fake_progress = eased_ratio * global.fake_progress_limit; - - # Update progress bar with fake progress - update_progress_bar(global.fake_progress); - } - } - - -Plymouth.SetRefreshFunction (refresh_callback); - -#----------------------------------------- Helper Functions -------------------------------- - -fun update_progress_bar(progress) - { - # Only update if progress is moving forward - if (progress > global.max_progress) - { - global.max_progress = progress; - width = Math.Int(progress_bar.original_image.GetWidth() * progress); - if (width < 1) width = 1; # Ensure minimum width of 1 pixel - - progress_bar.image = progress_bar.original_image.Scale(width, progress_bar.original_image.GetHeight()); - progress_bar.sprite.SetImage(progress_bar.image); - } - } - -fun show_progress_bar() - { - progress_box.sprite.SetOpacity(1); - progress_bar.sprite.SetOpacity(1); - } - -fun hide_progress_bar() - { - progress_box.sprite.SetOpacity(0); - progress_bar.sprite.SetOpacity(0); - } - -fun show_password_dialog() - { - lock.sprite.SetOpacity(1); - entry.sprite.SetOpacity(1); - } - -fun hide_password_dialog() - { - lock.sprite.SetOpacity(0); - entry.sprite.SetOpacity(0); - for (index = 0; bullet.sprites[index]; index++) - bullet.sprites[index].SetOpacity(0); - } - -fun start_fake_progress() - { - # Don't reset if we already have progress - if (global.max_progress == 0.0) - { - global.fake_progress = 0.0; - global.real_progress = 0.0; - update_progress_bar(0.0); - } - global.fake_progress_active = 1; - global.animation_frame = 0; - } - -fun stop_fake_progress() - { - global.fake_progress_active = 0; - } - -#----------------------------------------- Dialogue -------------------------------- - -lock.image = Image("lock.png"); -entry.image = Image("entry.png"); -bullet.image = Image("bullet.png"); - -entry.sprite = Sprite(entry.image); -entry.x = Window.GetWidth()/2 - entry.image.GetWidth() / 2; -entry.y = logo.sprite.GetY() + logo.image.GetHeight() + 40; -entry.sprite.SetPosition(entry.x, entry.y, 10001); -entry.sprite.SetOpacity(0); - -# Scale lock to be slightly shorter than entry field height -# Original lock is 84x96, entry height determines scale -lock_height = entry.image.GetHeight() * 0.8; -lock_scale = lock_height / 96; -lock_width = 84 * lock_scale; - -scaled_lock = lock.image.Scale(lock_width, lock_height); -lock.sprite = Sprite(scaled_lock); -lock.x = entry.x - lock_width - 15; -lock.y = entry.y + entry.image.GetHeight()/2 - lock_height/2; -lock.sprite.SetPosition(lock.x, lock.y, 10001); -lock.sprite.SetOpacity(0); - -# Bullet array -bullet.sprites = []; - -fun display_normal_callback () - { - hide_password_dialog(); - - # Get current mode - mode = Plymouth.GetMode(); - - # Only show progress bar for boot and resume modes - if (mode == "boot" || mode == "resume") - { - show_progress_bar(); - start_fake_progress(); - } - } - -fun display_password_callback (prompt, bullets) - { - global.password_shown = 1; # Mark that password dialog has been shown - - # Reset progress when password dialog appears - stop_fake_progress(); - hide_progress_bar(); - global.max_progress = 0.0; - global.fake_progress = 0.0; - global.real_progress = 0.0; - show_password_dialog(); - - # Clear all bullets first - for (index = 0; bullet.sprites[index]; index++) - bullet.sprites[index].SetOpacity(0); - - # Create and show bullets for current password (max 21) - max_bullets = 21; - bullets_to_show = bullets; - if (bullets_to_show > max_bullets) - bullets_to_show = max_bullets; - - for (index = 0; index < bullets_to_show; index++) - { - if (!bullet.sprites[index]) - { - # Scale bullet image to 7x7 pixels - scaled_bullet = bullet.image.Scale(7, 7); - bullet.sprites[index] = Sprite(scaled_bullet); - bullet.x = entry.x + 20 + index * (7 + 5); - bullet.y = entry.y + entry.image.GetHeight() / 2 - 3.5; - bullet.sprites[index].SetPosition(bullet.x, bullet.y, 10002); - } - bullet.sprites[index].SetOpacity(1); - } - } - -Plymouth.SetDisplayNormalFunction(display_normal_callback); -Plymouth.SetDisplayPasswordFunction(display_password_callback); - -#----------------------------------------- Progress Bar -------------------------------- - -progress_box.image = Image("progress_box.png"); -progress_box.sprite = Sprite(progress_box.image); - -progress_box.x = Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2; -progress_box.y = entry.y + entry.image.GetHeight() / 2 - progress_box.image.GetHeight() / 2; -progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0); -progress_box.sprite.SetOpacity(0); - -progress_bar.original_image = Image("progress_bar.png"); -progress_bar.sprite = Sprite(); -progress_bar.image = progress_bar.original_image.Scale(1, progress_bar.original_image.GetHeight()); - -progress_bar.x = Window.GetWidth() / 2 - progress_bar.original_image.GetWidth() / 2; -progress_bar.y = progress_box.y + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2; -progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1); -progress_bar.sprite.SetOpacity(0); - -fun progress_callback (duration, progress) - { - global.real_progress = progress; - - # If real progress is above limit, stop fake progress and use real progress - if (progress > global.fake_progress_limit) - { - stop_fake_progress(); - update_progress_bar(progress); - } - } - -Plymouth.SetBootProgressFunction(progress_callback); - -#----------------------------------------- Quit -------------------------------- - -fun quit_callback () -{ - logo.sprite.SetOpacity (1); -} - -Plymouth.SetQuitFunction(quit_callback); - -#----------------------------------------- Message -------------------------------- - -message_sprite = Sprite(); -message_sprite.SetPosition(10, 10, 10000); - -fun display_message_callback (text) -{ - my_image = Image.Text(text, 1, 1, 1); - message_sprite.SetImage(my_image); -} - -fun hide_message_callback (text) -{ - message_sprite.SetOpacity(0); -} - -Plymouth.SetDisplayMessageFunction (display_message_callback); -Plymouth.SetHideMessageFunction (hide_message_callback); - -# Initialize progress bar immediately for normal boots -if (Plymouth.GetMode() == "boot" || Plymouth.GetMode() == "resume") - { - show_progress_bar(); - start_fake_progress(); - } diff --git a/themes/engine/plymouth/progress_bar.png b/themes/engine/plymouth/progress_bar.png deleted file mode 100644 index dbb9fd7..0000000 Binary files a/themes/engine/plymouth/progress_bar.png and /dev/null differ diff --git a/themes/engine/plymouth/progress_box.png b/themes/engine/plymouth/progress_box.png deleted file mode 100644 index 6a263f2..0000000 Binary files a/themes/engine/plymouth/progress_box.png and /dev/null differ diff --git a/themes/engine/scripts.nix b/themes/engine/scripts.nix deleted file mode 100644 index 3156fd6..0000000 --- a/themes/engine/scripts.nix +++ /dev/null @@ -1,51 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - nomarchyLib = import ../../lib { inherit lib; }; - - # Dependencies for theme engine scripts - themeDeps = with pkgs; [ - coreutils - gnused - gnugrep - findutils - gawk - jq - swww - libnotify - gum - brightnessctl - playerctl - pamixer - bc - ]; - - nomarchy-theme-engine-scripts = pkgs.stdenv.mkDerivation { - pname = "nomarchy-theme-engine-scripts"; - version = "1.0.0"; - src = ./scripts; - - nativeBuildInputs = [ pkgs.makeWrapper ]; - - installPhase = '' - mkdir -p $out/bin - cp * $out/bin/ - - chmod +x $out/bin/* - patchShebangs $out/bin - ''; - - postFixup = '' - deps="${lib.makeBinPath themeDeps}" - for file in $out/bin/*; do - if [ -f "$file" ]; then - wrapProgram "$file" \ - --prefix PATH : "$deps" - fi - done - ''; - }; -in -{ - home.packages = [ nomarchy-theme-engine-scripts ]; -} diff --git a/themes/engine/scripts/nomarchy-font-current b/themes/engine/scripts/nomarchy-font-current deleted file mode 100755 index 83aff57..0000000 --- a/themes/engine/scripts/nomarchy-font-current +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e - -# Returns the name of the current monospace font being used by extracting it from the Waybar stylesheet. -# This can be changed using nomarchy-font-set. - -grep -oP 'font-family:\s*["'\'']?\K[^;"'\'']+' ~/.config/waybar/style.css | head -n1 diff --git a/themes/engine/scripts/nomarchy-font-list b/themes/engine/scripts/nomarchy-font-list deleted file mode 100755 index 78f7e55..0000000 --- a/themes/engine/scripts/nomarchy-font-list +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Returns a list of all the monospace fonts available on the system that can be set using nomarchy-font-set. - -fc-list :spacing=100 -f "%{family[0]}\n" | grep -v -i -E 'emoji|signwriting|nomarchy' | sort -u diff --git a/themes/engine/scripts/nomarchy-font-set b/themes/engine/scripts/nomarchy-font-set deleted file mode 100755 index 3bd096e..0000000 --- a/themes/engine/scripts/nomarchy-font-set +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Set the system-wide monospace font that should be used by the terminal, hyprlock, waybar, swayosd, etc. -# Declarative version for Nomarchy NixOS. -# Usage: nomarchy-font-set <font-name> [--no-update] - -font_name="$1" -no_update=false - -if [[ "${2:-}" == "--no-update" ]]; then - no_update=true -fi - -if [[ -z $font_name ]]; then - echo "Usage: nomarchy-font-set <font-name> [--no-update]" - exit 1 -fi - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" - -mkdir -p "$STATE_DIR" -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -if fc-list | grep -iq "$font_name"; then - 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." - exit 0 - fi - - nomarchy-env-update - - # Instant feedback for certain apps via IPC - if pgrep -x kitty; then - pkill -USR1 kitty - fi - if pgrep -x ghostty; then - pkill -SIGUSR2 ghostty - notify-send -u low " You must restart Ghostty to see font change" - fi - - nomarchy-hook font-set "$font_name" -else - echo "Font '$font_name' not found." - exit 1 -fi diff --git a/themes/engine/scripts/nomarchy-show-done b/themes/engine/scripts/nomarchy-show-done deleted file mode 100755 index d87c7d3..0000000 --- a/themes/engine/scripts/nomarchy-show-done +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Display a "Done!" message with a spinner and wait for user to press any key. -# Used by various install scripts to indicate completion. - -echo -gum spin --spinner "globe" --title "Done! Press any key to close..." -- bash -c 'read -n 1 -s' diff --git a/themes/engine/scripts/nomarchy-show-logo b/themes/engine/scripts/nomarchy-show-logo deleted file mode 100755 index 0e7dc31..0000000 --- a/themes/engine/scripts/nomarchy-show-logo +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -# Display the Nomarchy logo in the terminal using green color. -# Used by various presentation scripts to show branding. - -clear -echo -e "\033[32m" -cat < ~/.config/nomarchy/branding/logo.txt -echo -e "\033[0m" diff --git a/themes/engine/scripts/nomarchy-theme-bg-install b/themes/engine/scripts/nomarchy-theme-bg-install deleted file mode 100755 index bd1da72..0000000 --- a/themes/engine/scripts/nomarchy-theme-bg-install +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -CURRENT_THEME_NAME=$(cat "$HOME/.config/nomarchy/current/theme.name") -THEME_USER_BACKGROUNDS="$HOME/.config/nomarchy/backgrounds/$CURRENT_THEME_NAME" - -mkdir -p "$THEME_USER_BACKGROUNDS" -nautilus "$THEME_USER_BACKGROUNDS" diff --git a/themes/engine/scripts/nomarchy-theme-bg-next b/themes/engine/scripts/nomarchy-theme-bg-next deleted file mode 100755 index 963ae63..0000000 --- a/themes/engine/scripts/nomarchy-theme-bg-next +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Cycles through the background images available for the current theme. -# Declarative + Hybrid (instant swww) for Nomarchy NixOS. - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" -mkdir -p "$STATE_DIR" -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -THEME_NAME=$(jq -r '.theme // "summer-night"' "$STATE_FILE") - -# Resolve themes directory (Built-in from Nix store via Home Manager, or user extra) -if [ -d "$HOME/.config/nomarchy/themes/$THEME_NAME" ]; then - THEMES_DIR="$HOME/.config/nomarchy/themes" -else - THEMES_DIR="$HOME/.local/share/nomarchy/themes" -fi - -BG_DIR="$THEMES_DIR/$THEME_NAME/backgrounds" - -if [ ! -d "$BG_DIR" ]; then - notify-send "No background directory found for theme $THEME_NAME" - exit 1 -fi - -mapfile -t BACKGROUNDS < <(ls "$BG_DIR" | sort) -TOTAL=${#BACKGROUNDS[@]} - -if (( TOTAL == 0 )); then - notify-send "No backgrounds found in $BG_DIR" - exit 1 -fi - -CURRENT_BG=$(jq -r '.wallpaper' "$STATE_FILE") -INDEX=-1 -for i in "${!BACKGROUNDS[@]}"; do - if [[ "$BG_DIR/${BACKGROUNDS[$i]}" == "$CURRENT_BG" ]]; then - INDEX=$i - break - fi -done - -NEXT_INDEX=$(((INDEX + 1) % TOTAL)) -NEW_BG="$BG_DIR/${BACKGROUNDS[$NEXT_INDEX]}" - -nomarchy-state-write wallpaper "$NEW_BG" - -# Instant feedback via swww -if pgrep -x swww-daemon >/dev/null; then - swww img "$NEW_BG" --transition-type outer --transition-pos 0.85,0.97 --transition-step 90 -else - swww init && swww img "$NEW_BG" -fi - -echo "Background set to $NEW_BG declaratively." diff --git a/themes/engine/scripts/nomarchy-theme-bg-set b/themes/engine/scripts/nomarchy-theme-bg-set deleted file mode 100755 index ebf64aa..0000000 --- a/themes/engine/scripts/nomarchy-theme-bg-set +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -set -e - -# Sets the specified image as the current background. -# Updates state.json so the choice survives the next rebuild — without -# that write, themes/engine/files.nix re-resolves the wallpaper from -# `nomarchy.wallpaper` on the next home-manager switch and silently -# falls back to the active theme's default background, undoing the -# user's pick. - -if [[ -z $1 ]]; then - echo "Usage: nomarchy-theme-bg-set <path-to-image>" >&2 - exit 1 -fi - -BACKGROUND="$1" - -if [[ ! -f "$BACKGROUND" ]]; then - echo "Background image not found: $BACKGROUND" >&2 - exit 1 -fi - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" -CURRENT_BACKGROUND_LINK="$STATE_DIR/current/background" - -mkdir -p "$STATE_DIR" -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -# Persist the choice for the next rebuild. -nomarchy-state-write wallpaper "$BACKGROUND" - -# Create symlink to the new background -ln -nsf "$BACKGROUND" "$CURRENT_BACKGROUND_LINK" - -# Kill existing swaybg and start new one -pkill -x swaybg -setsid uwsm-app -- swaybg -i "$CURRENT_BACKGROUND_LINK" -m fill >/dev/null 2>&1 & diff --git a/themes/engine/scripts/nomarchy-theme-current b/themes/engine/scripts/nomarchy-theme-current deleted file mode 100755 index d66dcc1..0000000 --- a/themes/engine/scripts/nomarchy-theme-current +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -THEME_NAME_PATH="$HOME/.config/nomarchy/current/theme.name" - -if [[ -f $THEME_NAME_PATH ]]; then - cat $THEME_NAME_PATH | sed -E 's/(^|-)([a-z])/\1\u\2/g; s/-/ /g' -else - echo "Unknown" -fi diff --git a/themes/engine/scripts/nomarchy-theme-install b/themes/engine/scripts/nomarchy-theme-install deleted file mode 100755 index 874ef89..0000000 --- a/themes/engine/scripts/nomarchy-theme-install +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -set -e - -# nomarchy-theme-install: Install a new theme from a git repo for Nomarchy -# Usage: nomarchy-theme-install <git-repo-url> - -if [[ -z $1 ]]; then - echo -e "\e[32mSee https://manuals.omamix.org/2/the-nomarchy-manual/90/extra-themes\n\e[0m" - REPO_URL=$(gum input --placeholder="Git repo URL for theme" --header="") -else - REPO_URL="$1" -fi - -if [[ -z $REPO_URL ]]; then - exit 1 -fi - -THEMES_DIR="$HOME/.config/nomarchy/themes" -THEME_NAME=$(basename "$REPO_URL" .git | sed -E 's/^nomarchy-//; s/-theme$//') -THEME_PATH="$THEMES_DIR/$THEME_NAME" - -# Remove existing theme if present -if [[ -d $THEME_PATH ]]; then - rm -rf "$THEME_PATH" -fi - -# Clone the repo directly to ~/.config/nomarchy/themes -if ! git clone "$REPO_URL" "$THEME_PATH"; then - echo "Error: Failed to clone theme repo." - exit 1 -fi - -# Apply the new theme with nomarchy-theme-set -nomarchy-theme-set $THEME_NAME diff --git a/themes/engine/scripts/nomarchy-theme-list b/themes/engine/scripts/nomarchy-theme-list deleted file mode 100755 index fef3926..0000000 --- a/themes/engine/scripts/nomarchy-theme-list +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -{ - find ~/.config/nomarchy/themes/ -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -printf '%f\n' 2>/dev/null || true - find ~/.local/share/nomarchy/themes/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null || true -} | sort -u | while read -r name; do - echo "$name" | sed -E 's/(^|-)([a-z])/\1\u\2/g; s/-/ /g' -done diff --git a/themes/engine/scripts/nomarchy-theme-next b/themes/engine/scripts/nomarchy-theme-next deleted file mode 100755 index fe35be3..0000000 --- a/themes/engine/scripts/nomarchy-theme-next +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -e - -# Cycles to the next available theme (wraps from last back to first). - -mapfile -t THEMES < <(nomarchy-theme-list) -TOTAL=${#THEMES[@]} - -if (( TOTAL == 0 )); then - notify-send "No themes available" - exit 1 -fi - -CURRENT=$(nomarchy-theme-current) -INDEX=-1 -for i in "${!THEMES[@]}"; do - if [[ "${THEMES[$i]}" == "$CURRENT" ]]; then - INDEX=$i - break - fi -done - -NEXT_INDEX=$(((INDEX + 1) % TOTAL)) -nomarchy-theme-set "${THEMES[$NEXT_INDEX]}" diff --git a/themes/engine/scripts/nomarchy-theme-refresh b/themes/engine/scripts/nomarchy-theme-refresh deleted file mode 100755 index 6b37f61..0000000 --- a/themes/engine/scripts/nomarchy-theme-refresh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -# Refresh the current theme from its templates. - -THEME_NAME_PATH="$HOME/.config/nomarchy/current/theme.name" - -if [[ -f $THEME_NAME_PATH ]]; then - nomarchy-theme-set "$(cat $THEME_NAME_PATH)" -fi diff --git a/themes/engine/scripts/nomarchy-theme-remove b/themes/engine/scripts/nomarchy-theme-remove deleted file mode 100755 index c95fce3..0000000 --- a/themes/engine/scripts/nomarchy-theme-remove +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -# nomarchy-theme-remove: Remove a theme from Nomarchy by name -# Usage: nomarchy-theme-remove <theme-name> - -if [[ -z $1 ]]; then - mapfile -t extra_themes < <(find ~/.config/nomarchy/themes -mindepth 1 -maxdepth 1 -type d ! -xtype l -printf '%f\n') - - if (( ${#extra_themes[@]} > 0 )); then - THEME_NAME=$(printf '%s\n' "${extra_themes[@]}" | sort | gum choose --header="Remove extra theme") - else - echo "No extra themes installed." - exit 1 - fi -else - THEME_NAME="$1" -fi - -THEMES_DIR="$HOME/.config/nomarchy/themes" -CURRENT_DIR="$HOME/.config/nomarchy/current" -THEME_PATH="$THEMES_DIR/$THEME_NAME" - -# Ensure a theme was set -if [[ -z $THEME_NAME ]]; then - exit 1 -fi - -# Check if theme exists before attempting removal -if [[ ! -d $THEME_PATH ]]; then - echo "Error: Theme '$THEME_NAME' not found." - exit 1 -fi - -# Now remove the theme directory for THEME_NAME -rm -rf "$THEME_PATH" diff --git a/themes/engine/scripts/nomarchy-theme-set b/themes/engine/scripts/nomarchy-theme-set deleted file mode 100755 index a75bfd5..0000000 --- a/themes/engine/scripts/nomarchy-theme-set +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Set the system theme declaratively. -# Usage: nomarchy-theme-set <theme-name> [--no-update] - -THEME_NAME="$1" -NO_UPDATE=false - -if [[ "${2:-}" == "--no-update" ]]; then - NO_UPDATE=true -fi - -if [[ -z $THEME_NAME ]]; then - echo "Usage: nomarchy-theme-set <theme-name> [--no-update]" - exit 1 -fi - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" - -# Resolve themes directory (Built-in from Nix store via Home Manager, or user extra) -if [ -d "$HOME/.config/nomarchy/themes/$THEME_NAME" ]; then - THEMES_DIR="$HOME/.config/nomarchy/themes" -else - THEMES_DIR="$HOME/.local/share/nomarchy/themes" -fi - -mkdir -p "$STATE_DIR" -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -if [ ! -d "$THEMES_DIR/$THEME_NAME" ]; then - echo "Theme '$THEME_NAME' not found in $THEMES_DIR" >&2 - exit 1 -fi - -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 - nomarchy-state-write --file "$SYSTEM_STATE_FILE" theme "$THEME_NAME" -fi - -# Try to find a background for this theme -BG_DIR="$THEMES_DIR/$THEME_NAME/backgrounds" -if [ -d "$BG_DIR" ]; then - BG=$(ls "$BG_DIR" | head -n 1) - if [ -n "$BG" ]; then - nomarchy-state-write wallpaper "$BG_DIR/$BG" - fi -fi - -echo "Theme set to $THEME_NAME. Applying changes with nomarchy-env-update..." - -if [[ "$NO_UPDATE" == "true" ]]; then - echo "Skipping nomarchy-env-update due to --no-update flag." - exit 0 -fi - -nomarchy-env-update - -nomarchy-theme-set-templates - -# Run the chain of "tell each app the theme changed" steps. Each step is -# optional — the corresponding helper might not be installed, the service -# might not be running, the user might not use Obsidian. Skipping is fine; -# what we don't want is a step that EXISTS, fails for a real reason, and -# leaves the user with a half-applied theme + no idea why. Collect every -# real failure into _theme_set_fails and surface them in one notify-send -# at the end so the user knows exactly what didn't refresh. -_theme_set_fails=() - -_theme_set_try() { - local label="$1"; shift - # First arg after label is the command to test for existence. - # `systemctl` always exists on a NixOS system, so we let it through - # and rely on its own exit code to decide skip vs fail. - if [[ "$1" != "systemctl" ]] && ! command -v "$1" >/dev/null 2>&1; then - return 0 # not installed → silently skip - fi - if ! "$@" >/dev/null 2>&1; then - _theme_set_fails+=("$label") - fi -} - -# Walker reads its CSS via @import of ~/.config/nomarchy/current/theme/apps/walker/style.css, -# and waybar's shared fallback style does the same with waybar.css. HM's sd-switch -# only restarts services whose unit *definition* changed, so when only the imported -# file's contents change, neither gets reloaded. Restart them explicitly. -_theme_set_try "Walker" nomarchy-restart-walker -_theme_set_try "Waybar" nomarchy-restart-waybar - -# Hot-reload long-running TUIs / agents that read their colors from -# the active-theme symlink and would otherwise stay on the old palette -# until the user restarts them by hand. -_theme_set_try "btop" nomarchy-restart-btop -_theme_set_try "opencode" nomarchy-restart-opencode - -# Sync palette into Obsidian vaults (no-op when the user has no Obsidian -# config or no obsidian.css template in the active theme). -_theme_set_try "Obsidian" nomarchy-theme-set-obsidian - -# Reload the wallpaper — its ExecStart path is stable (~/.config/nomarchy/current/background) -# so sd-switch does not detect a unit change when only the symlink target moves. -_theme_set_try "wallpaper" systemctl --user restart nomarchy-wallpaper.service - -if (( ${#_theme_set_fails[@]} > 0 )); then - if command -v notify-send >/dev/null 2>&1; then - notify-send -u normal "Theme applied with warnings" \ - "Did not refresh: $(IFS=', '; echo "${_theme_set_fails[*]}")" - fi - echo "Warning: failed to refresh: ${_theme_set_fails[*]}" >&2 -fi - -nomarchy-hook theme-set "$THEME_NAME" diff --git a/themes/engine/scripts/nomarchy-theme-set-keyboard b/themes/engine/scripts/nomarchy-theme-set-keyboard deleted file mode 100755 index a9545f3..0000000 --- a/themes/engine/scripts/nomarchy-theme-set-keyboard +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -e - -nomarchy-theme-set-keyboard-asus-rog -nomarchy-theme-set-keyboard-f16 diff --git a/themes/engine/scripts/nomarchy-theme-set-keyboard-asus-rog b/themes/engine/scripts/nomarchy-theme-set-keyboard-asus-rog deleted file mode 100755 index 389b867..0000000 --- a/themes/engine/scripts/nomarchy-theme-set-keyboard-asus-rog +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -ASUSCTL_THEME=~/.config/nomarchy/current/theme/keyboard.rgb - -if nomarchy-cmd-present asusctl; then - asusctl aura effect static -c $(sed 's/^#//' $ASUSCTL_THEME) -fi diff --git a/themes/engine/scripts/nomarchy-theme-set-keyboard-f16 b/themes/engine/scripts/nomarchy-theme-set-keyboard-f16 deleted file mode 100755 index ab30182..0000000 --- a/themes/engine/scripts/nomarchy-theme-set-keyboard-f16 +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -set -e - -FRAMEWORK16_THEME=~/.config/nomarchy/current/theme/keyboard.rgb - -if nomarchy-cmd-present qmk_hid && [[ -f $FRAMEWORK16_THEME ]]; then - hex=$(cat "$FRAMEWORK16_THEME") - hex="${hex#\#}" - - # Convert hex to QMK HSV (0-255 scale) using Python's colorsys - read -r h s <<< $(python3 -c " -import colorsys -r, g, b = int('$hex'[:2],16)/255, int('$hex'[2:4],16)/255, int('$hex'[4:6],16)/255 -h, s, v = colorsys.rgb_to_hsv(r, g, b) -print(int(h * 255), int(s * 255)) -") - - qmk_hid via --rgb-effect 1 2>/dev/null - qmk_hid via --rgb-hue "$h" 2>/dev/null - qmk_hid via --rgb-saturation "$s" 2>/dev/null - qmk_hid via --rgb-brightness 100 2>/dev/null - qmk_hid via --save 2>/dev/null -fi diff --git a/themes/engine/scripts/nomarchy-theme-set-obsidian b/themes/engine/scripts/nomarchy-theme-set-obsidian deleted file mode 100755 index b7bcf6f..0000000 --- a/themes/engine/scripts/nomarchy-theme-set-obsidian +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -set -e - -# Sync Nomarchy theme to all Obsidian vaults - -CURRENT_THEME_DIR="$HOME/.config/nomarchy/current/theme" - -[[ -f $CURRENT_THEME_DIR/obsidian.css ]] || exit 0 - -jq -r '.vaults | values[].path' ~/.config/obsidian/obsidian.json 2>/dev/null | while read -r vault_path; do - [[ -d $vault_path/.obsidian ]] || continue - - theme_dir="$vault_path/.obsidian/themes/Nomarchy" - mkdir -p "$theme_dir" - - [[ -f $theme_dir/manifest.json ]] || cat >"$theme_dir/manifest.json" <<'EOF' -{ - "name": "Nomarchy", - "version": "1.0.0", - "minAppVersion": "0.16.0", - "description": "Automatically syncs with your current Nomarchy system theme colors and fonts", - "author": "Nomarchy", - "authorUrl": "https://nomarchy.org" -} -EOF - - cp "$CURRENT_THEME_DIR/obsidian.css" "$theme_dir/theme.css" -done diff --git a/themes/engine/scripts/nomarchy-theme-set-templates b/themes/engine/scripts/nomarchy-theme-set-templates deleted file mode 100755 index e57e529..0000000 --- a/themes/engine/scripts/nomarchy-theme-set-templates +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -set -e - -TEMPLATES_DIR="$HOME/.local/share/nomarchy/templates" -USER_TEMPLATES_DIR="$HOME/.config/nomarchy/themes/templates" -NEXT_THEME_DIR="$HOME/.config/nomarchy/current/theme" -COLORS_FILE="$NEXT_THEME_DIR/colors.toml" - -# Convert hex color to decimal RGB (e.g., "#1e1e2e" -> "30,30,46") -hex_to_rgb() { - local hex="${1#\#}" - printf "%d,%d,%d" "0x${hex:0:2}" "0x${hex:2:2}" "0x${hex:4:2}" -} - -# Only generate dynamic templates for themes with a colors.toml definition -if [[ -f $COLORS_FILE ]]; then - sed_script=$(mktemp) - - while IFS='=' read -r key value; do - key="${key//[\"\' ]/}" # strip quotes and spaces from key - [[ $key && $key != \#* ]] || continue # skip empty lines and comments - value="${value#*[\"\']}" - value="${value%%[\"\']*}" # extract value between quotes (ignores inline comments) - - printf 's|{{ %s }}|%s|g\n' "$key" "$value" # {{ key }} -> value - printf 's|{{ %s_strip }}|%s|g\n' "$key" "${value#\#}" # {{ key_strip }} -> value without leading # - if [[ $value =~ ^# ]]; then - rgb=$(hex_to_rgb "$value") - echo "s|{{ ${key}_rgb }}|${rgb}|g" - fi - done <"$COLORS_FILE" >"$sed_script" - - shopt -s nullglob - - # Process user templates first, then built-in templates (user overrides built-in) - for tpl in "$USER_TEMPLATES_DIR"/*.tpl "$TEMPLATES_DIR"/*.tpl; do - filename=$(basename "$tpl" .tpl) - output_path="$NEXT_THEME_DIR/$filename" - - # Don't overwrite configs already exists in the output directory (copied from theme specific folder) - if [[ ! -f $output_path ]]; then - sed -f "$sed_script" "$tpl" >"$output_path" - fi - done - - rm "$sed_script" -fi diff --git a/themes/engine/scripts/nomarchy-theme-update b/themes/engine/scripts/nomarchy-theme-update deleted file mode 100755 index 03132e5..0000000 --- a/themes/engine/scripts/nomarchy-theme-update +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -for dir in ~/.config/nomarchy/themes/*/; do - if [[ -d $dir ]] && [[ ! -L ${dir%/} ]] && [[ -d $dir/.git ]]; then - echo "Updating: $(basename "$dir")" - git -C "$dir" pull - fi -done diff --git a/themes/engine/scripts/nomarchy-themes-prebuild b/themes/engine/scripts/nomarchy-themes-prebuild deleted file mode 100755 index 5eaaca0..0000000 --- a/themes/engine/scripts/nomarchy-themes-prebuild +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# Pre-realise every theme's Home Manager generation into the Nix store so -# future `nomarchy-theme-set` swaps are cache hits — no Stylix rebuild, no -# downloads. Run once after install (the installer's final message prompts -# you to do so) and again after adding or updating palettes. - -set -e - -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." >&2 - exit 1 -fi - -echo "Pre-building every theme variant from $REPO_DIR#allThemeVariants..." -echo "This is a one-time cache warm-up. Subsequent theme switches will be instant." -nix build --no-link --print-out-paths \ - --extra-experimental-features "nix-command flakes" \ - "$REPO_DIR#allThemeVariants" - -echo "Theme variants cached. Theme switches now avoid rebuilds." diff --git a/themes/engine/scripts/nomarchy-toggle-nightlight b/themes/engine/scripts/nomarchy-toggle-nightlight deleted file mode 100755 index 2edd50b..0000000 --- a/themes/engine/scripts/nomarchy-toggle-nightlight +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Toggles the nightlight (hyprsunset). Writes the new state to state.json -# (so the next home-manager rebuild realigns services.hyprsunset.enable -# via features/desktop/nightlight.nix) and flips the running systemd -# user unit for instant feedback. Reads nightlightTemperature from -# state.json so the value the user picked is honoured instead of a -# hardcoded constant. - -STATE_DIR="$HOME/.config/nomarchy" -STATE_FILE="$STATE_DIR/state.json" -mkdir -p "$STATE_DIR" - -[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - -if [[ $NOMARCHY_TOGGLE_NIGHTLIGHT == "false" ]]; then - NEW_VALUE="true" - systemctl --user start hyprsunset.service 2>/dev/null || true - TEMP=$(jq -r '.nightlightTemperature // 4000' "$STATE_FILE") - notify-send -u low " Nightlight enabled (${TEMP}K)" -else - NEW_VALUE="false" - systemctl --user stop hyprsunset.service 2>/dev/null || true - notify-send -u low " Nightlight disabled" -fi - -nomarchy-state-write nightlight "$NEW_VALUE" --type bool - -echo "Nightlight state set to $NEW_VALUE." diff --git a/themes/engine/sddm.nix b/themes/engine/sddm.nix deleted file mode 100644 index 1b114f9..0000000 --- a/themes/engine/sddm.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - nomarchy-sddm-theme = pkgs.stdenv.mkDerivation { - pname = "nomarchy-sddm-theme"; - version = "1.0"; - src = ./sddm/nomarchy; - nativeBuildInputs = [ pkgs.libsForQt5.qt5.wrapQtAppsHook ]; - installPhase = '' - mkdir -p $out/share/sddm/themes/nomarchy - cp -r * $out/share/sddm/themes/nomarchy/ - ''; - propagatedBuildInputs = with pkgs.libsForQt5.qt5; [ - qtgraphicaleffects - qtquickcontrols2 - qtsvg - ]; - }; -in -{ - services.xserver.enable = lib.mkDefault true; - services.displayManager.sddm = { - enable = lib.mkDefault true; - wayland.enable = lib.mkDefault true; - theme = lib.mkDefault "nomarchy"; - }; - - services.displayManager.defaultSession = lib.mkDefault "hyprland-uwsm"; - - # autoLogin defaults off so hand-migrated configs (no installer-written - # username) don't try to log in as a nonexistent "nomarchy" user. The - # installer-generated system.nix sets both `enable = true;` and - # `user = "$USERNAME";` at normal priority, overriding these defaults. - services.displayManager.autoLogin = { - enable = lib.mkDefault false; - user = lib.mkDefault "nomarchy"; - }; - - # Not mkDefault: systemPackages is a list every module contributes to at - # normal priority, so a mkDefault def is dropped entirely — leaving the - # SDDM theme package uninstalled and the greeter unthemed. - environment.systemPackages = [ nomarchy-sddm-theme ]; - - # Enable Hyprland system-level dependencies - programs.hyprland = { - enable = lib.mkDefault true; - withUWSM = lib.mkDefault true; - }; - programs.uwsm.enable = lib.mkDefault true; -} diff --git a/themes/engine/sddm/nomarchy/Main.qml b/themes/engine/sddm/nomarchy/Main.qml deleted file mode 100644 index 7f4da8c..0000000 --- a/themes/engine/sddm/nomarchy/Main.qml +++ /dev/null @@ -1,99 +0,0 @@ -import QtQuick 2.0 -import SddmComponents 2.0 - -Rectangle { - id: root - width: 640 - height: 480 - color: "#000000" - - property string currentUser: userModel.lastUser - property int sessionIndex: { - for (var i = 0; i < sessionModel.rowCount(); i++) { - var name = (sessionModel.data(sessionModel.index(i, 0), Qt.DisplayRole) || "").toString() - if (name.toLowerCase().indexOf("uwsm") !== -1) - return i - } - return Math.max(0, sessionModel.lastIndex) - } - - Connections { - target: sddm - function onLoginFailed() { - errorMessage.text = "Login failed" - password.text = "" - password.focus = true - } - function onLoginSucceeded() { - errorMessage.text = "" - } - } - - Column { - anchors.centerIn: parent - spacing: root.height * 0.04 - width: parent.width - - Image { - source: "logo.svg" - width: root.width * 0.35 - height: Math.round(width * sourceSize.height / sourceSize.width) - fillMode: Image.PreserveAspectFit - anchors.horizontalCenter: parent.horizontalCenter - } - - Row { - anchors.horizontalCenter: parent.horizontalCenter - spacing: root.width * 0.007 - - Text { - text: "\uf023" - color: "#ffffff" - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: root.height * 0.025 - anchors.verticalCenter: parent.verticalCenter - } - - Rectangle { - width: root.width * 0.17 - height: root.height * 0.04 - color: "#000000" - border.color: "#ffffff" - border.width: 1 - clip: true - - TextInput { - id: password - anchors.fill: parent - anchors.margins: root.height * 0.008 - verticalAlignment: TextInput.AlignVCenter - echoMode: TextInput.Password - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: root.height * 0.02 - font.letterSpacing: root.height * 0.004 - passwordCharacter: "\u2022" - color: "#ffffff" - focus: true - - Keys.onPressed: { - if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { - sddm.login(root.currentUser, password.text, root.sessionIndex) - event.accepted = true - } - } - } - } - } - - Text { - id: errorMessage - text: "" - color: "#f7768e" - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: root.height * 0.018 - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Component.onCompleted: password.forceActiveFocus() -} diff --git a/themes/engine/sddm/nomarchy/logo.svg b/themes/engine/sddm/nomarchy/logo.svg deleted file mode 100644 index b060ba0..0000000 --- a/themes/engine/sddm/nomarchy/logo.svg +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - width="157.40488mm" - height="162.48518mm" - viewBox="0 0 157.40488 162.48518" - version="1.1" - id="svg1" - xml:space="preserve" - inkscape:version="1.4.3 (0d15f75042, 2025-12-25)" - sodipodi:docname="icon.svg" - inkscape:export-filename="logo.png" - inkscape:export-xdpi="96" - inkscape:export-ydpi="96" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview - id="namedview1" - pagecolor="#ffffff" - bordercolor="#000000" - borderopacity="0.25" - inkscape:showpageshadow="2" - inkscape:pageopacity="0.0" - inkscape:pagecheckerboard="0" - inkscape:deskcolor="#d1d1d1" - inkscape:document-units="mm" - inkscape:zoom="0.82803284" - inkscape:cx="216.175" - inkscape:cy="452.27675" - inkscape:window-width="1914" - inkscape:window-height="1012" - inkscape:window-x="0" - inkscape:window-y="0" - inkscape:window-maximized="0" - inkscape:current-layer="layer1"><inkscape:page - x="0" - y="-1.1741086e-21" - width="157.40488" - height="162.48518" - id="page2" - margin="0" - bleed="0" /></sodipodi:namedview><defs - id="defs1" /><g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-28.332214,-25.599269)"><g - id="g4" - transform="translate(3.1953242,-22.686801)" - style="fill:#000088;fill-opacity:1"><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 25.136891,85.823024 25.557592,210.77125 88.452409,174.38061 67.417351,160.49747 57.110174,166.38729 V 105.80633 Z" - id="path1" /><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 67.728991,112.41131 182.54178,185.60757 153.16137,202.85259 67.830432,148.17947 Z" - id="path2" /><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 139.74857,145.88014 140.00405,110.4959 54.800856,56.333749 25.675926,73.32329 Z" - id="path3" /><path - style="fill:#000088;fill-opacity:1;stroke-width:0.264583" - d="M 182.2863,172.70573 V 48.286069 l -62.59305,36.406166 20.82177,13.668277 10.21927,-5.74834 0.12774,60.165978 z" - id="path4" /></g></g></svg> diff --git a/themes/engine/sddm/nomarchy/metadata.desktop b/themes/engine/sddm/nomarchy/metadata.desktop deleted file mode 100644 index 4355b74..0000000 --- a/themes/engine/sddm/nomarchy/metadata.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[SddmGreeterTheme] -Name=Nomarchy -Description=Minimal terminal-style login theme matching the Limine bootloader aesthetic -Author=Nomarchy -Type=sddm-theme -Interface=Qt5 -Version=1.0 diff --git a/themes/engine/sddm/nomarchy/theme.conf b/themes/engine/sddm/nomarchy/theme.conf deleted file mode 100644 index e94cbbd..0000000 --- a/themes/engine/sddm/nomarchy/theme.conf +++ /dev/null @@ -1 +0,0 @@ -[General] diff --git a/themes/engine/stylix-compat.nix b/themes/engine/stylix-compat.nix deleted file mode 100644 index ad0d9e6..0000000 --- a/themes/engine/stylix-compat.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ config, lib, ... }: - -# Compatibility shims for stylix target modules -# Stylix unconditionally imports all target modules, which expect certain -# program options to exist. This module defines stub options for programs -# we don't use to prevent evaluation errors. - -{ - options = { - # Neovim: stylix uses initLua but home-manager uses extraLuaConfig - programs.neovim.initLua = lib.mkOption { - type = lib.types.lines; - default = ""; - description = "Lua code to run at init (compatibility shim for stylix)"; - }; - - # OpenCode: stylix expects programs.opencode options - programs.opencode = { - tui = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - default = {}; - description = "OpenCode TUI settings (stylix compatibility shim)"; - }; - themes = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - default = {}; - description = "OpenCode themes (stylix compatibility shim)"; - }; - }; - }; - - config = lib.mkIf (config.programs.neovim.initLua != "") { - programs.neovim.extraLuaConfig = config.programs.neovim.initLua; - }; -} diff --git a/themes/engine/stylix.nix b/themes/engine/stylix.nix deleted file mode 100644 index 84cd213..0000000 --- a/themes/engine/stylix.nix +++ /dev/null @@ -1,109 +0,0 @@ -{ config, pkgs, inputs, lib, ... }: - -# Stylix Integration Module -# -# This module handles base-level theming through Stylix: -# - Color scheme injection from the active theme's palette -# - Cursor configuration -# - Font configuration -# - GTK/GNOME theming -# -# App-specific theming is handled separately: -# - theme-loader.nix: Deploys theme's apps/ configs (btop, neovim, etc.) -# - waybar.nix: Generates waybar CSS from colorScheme -# - hyprland.nix: Handles hyprland border colors -# -# Stylix targets disabled here (we have custom implementations): -# - hyprland: Custom border/rule config -# - waybar: Custom CSS with theme colors - -let - nomarchyLib = import ../../lib { inherit lib; }; - assetsPath = ../palettes; - - activeThemeName = config.nomarchy.theme; - - # Use shared wallpaper resolver - activeWallpaper = nomarchyLib.resolveWallpaper { - wallpaperPath = config.nomarchy.wallpaper; - themeName = activeThemeName; - inherit assetsPath; - }; - - # Get palette using shared library - currentPalette = nomarchyLib.getPalette activeThemeName; -in -{ - imports = [ inputs.stylix.homeModules.stylix ]; - - xdg.configFile."nomarchy/current/background".source = activeWallpaper; - - stylix = { - enable = lib.mkDefault true; - enableReleaseChecks = lib.mkDefault false; - autoEnable = lib.mkDefault false; # Disable auto-detection, explicitly enable targets - image = lib.mkDefault activeWallpaper; - base16Scheme = lib.mkDefault currentPalette; - - # Use detected light mode state - polarity = lib.mkDefault (if config.nomarchy.isLightMode then "light" else "dark"); - - cursor = lib.mkDefault { - package = config.nomarchy.cursor.package; - name = config.nomarchy.cursor.name; - size = 24; - }; - - fonts = lib.mkDefault { - monospace = { - package = pkgs.nerd-fonts.jetbrains-mono; - name = config.nomarchy.fonts.monospace; - }; - sansSerif = { - package = pkgs.dejavu_fonts; - name = "DejaVu Sans"; - }; - serif = { - package = pkgs.dejavu_fonts; - name = "DejaVu Serif"; - }; - emoji = { - package = pkgs.noto-fonts-color-emoji; - name = "Noto Color Emoji"; - }; - sizes = { - applications = 11; - terminal = 11; - desktop = 11; - popups = 11; - }; - }; - - # Enable theming for specific targets - targets = lib.mkDefault { - hyprland.enable = false; # We keep our custom hyprland config for borders/rules - waybar.enable = false; # We keep our custom waybar CSS - neovim.enable = false; # We deploy theme lua files via theme-loader instead - neovide.enable = false; # Neovide depends on neovim program module - alacritty.enable = true; - kitty.enable = true; - gtk.enable = true; - gnome.enable = true; - }; - }; - - # GTK Icon Theme configuration. The package is derived from the theme's - # declared icon family (palettes/<theme>/icons.theme), so switching to a - # palette that needs e.g. Everforest icons automatically installs - # `everforest-gtk-variant` instead of sticking with `yaru-theme`. - gtk = { - enable = lib.mkDefault true; - iconTheme = lib.mkDefault { - package = nomarchyLib.iconThemePackage { - iconsTheme = config.nomarchy.iconsTheme; - inherit pkgs; - }; - name = config.nomarchy.iconsTheme; - }; - }; -} diff --git a/themes/ethereal.json b/themes/ethereal.json new file mode 100644 index 0000000..2b8fdc0 --- /dev/null +++ b/themes/ethereal.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Ethereal", + "slug": "ethereal", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#060B1E", + "mantle": "#05091a", + "surface": "#060B1E", + "overlay": "#6d7db6", + "text": "#ffcead", + "subtext": "#F99957", + "muted": "#6d7db6", + "accent": "#7d82d9", + "accentAlt": "#c89dc1", + "good": "#92a593", + "warn": "#E9BB4F", + "bad": "#ED5B5A" + }, + "ansi": [ + "#060B1E", + "#ED5B5A", + "#92a593", + "#E9BB4F", + "#7d82d9", + "#c89dc1", + "#a3bfd1", + "#F99957", + "#6d7db6", + "#faaaa9", + "#c4cfc4", + "#f7dc9c", + "#c2c4f0", + "#ead7e7", + "#dfeaf0", + "#ffcead" + ] +} diff --git a/themes/palettes/ethereal/backgrounds/1-cosmic.jpg b/themes/ethereal/backgrounds/1-cosmic.jpg similarity index 100% rename from themes/palettes/ethereal/backgrounds/1-cosmic.jpg rename to themes/ethereal/backgrounds/1-cosmic.jpg diff --git a/themes/palettes/ethereal/backgrounds/2-meadow.jpg b/themes/ethereal/backgrounds/2-meadow.jpg similarity index 100% rename from themes/palettes/ethereal/backgrounds/2-meadow.jpg rename to themes/ethereal/backgrounds/2-meadow.jpg diff --git a/themes/palettes/ethereal/apps/btop.theme b/themes/ethereal/btop.theme similarity index 100% rename from themes/palettes/ethereal/apps/btop.theme rename to themes/ethereal/btop.theme diff --git a/themes/everforest.json b/themes/everforest.json new file mode 100644 index 0000000..054406e --- /dev/null +++ b/themes/everforest.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Everforest", + "slug": "everforest", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#2d353b", + "mantle": "#262d32", + "surface": "#475258", + "overlay": "#475258", + "text": "#d3c6aa", + "subtext": "#d3c6aa", + "muted": "#475258", + "accent": "#7fbbb3", + "accentAlt": "#d699b6", + "good": "#a7c080", + "warn": "#dbbc7f", + "bad": "#e67e80" + }, + "ansi": [ + "#475258", + "#e67e80", + "#a7c080", + "#dbbc7f", + "#7fbbb3", + "#d699b6", + "#83c092", + "#d3c6aa", + "#475258", + "#e67e80", + "#a7c080", + "#dbbc7f", + "#7fbbb3", + "#d699b6", + "#83c092", + "#d3c6aa" + ] +} diff --git a/themes/palettes/everforest/backgrounds/1-tree-tops.jpg b/themes/everforest/backgrounds/1-tree-tops.jpg similarity index 100% rename from themes/palettes/everforest/backgrounds/1-tree-tops.jpg rename to themes/everforest/backgrounds/1-tree-tops.jpg diff --git a/themes/palettes/everforest/apps/btop.theme b/themes/everforest/btop.theme similarity index 100% rename from themes/palettes/everforest/apps/btop.theme rename to themes/everforest/btop.theme diff --git a/themes/flexoki-light.json b/themes/flexoki-light.json new file mode 100644 index 0000000..e972540 --- /dev/null +++ b/themes/flexoki-light.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Flexoki Light", + "slug": "flexoki-light", + "mode": "light", + "wallpaper": "", + "colors": { + "base": "#FFFCF0", + "mantle": "#d9d6cc", + "surface": "#100F0F", + "overlay": "#100F0F", + "text": "#100F0F", + "subtext": "#FFFCF0", + "muted": "#100F0F", + "accent": "#205EA6", + "accentAlt": "#CE5D97", + "good": "#879A39", + "warn": "#D0A215", + "bad": "#D14D41" + }, + "ansi": [ + "#100F0F", + "#D14D41", + "#879A39", + "#D0A215", + "#205EA6", + "#CE5D97", + "#3AA99F", + "#FFFCF0", + "#100F0F", + "#D14D41", + "#879A39", + "#D0A215", + "#4385BE", + "#CE5D97", + "#3AA99F", + "#FFFCF0" + ] +} diff --git a/themes/palettes/flexoki-light/backgrounds/1-orb.png b/themes/flexoki-light/backgrounds/1-orb.png similarity index 100% rename from themes/palettes/flexoki-light/backgrounds/1-orb.png rename to themes/flexoki-light/backgrounds/1-orb.png diff --git a/themes/palettes/flexoki-light/backgrounds/2-nomarchy.png b/themes/flexoki-light/backgrounds/2-nomarchy.png similarity index 100% rename from themes/palettes/flexoki-light/backgrounds/2-nomarchy.png rename to themes/flexoki-light/backgrounds/2-nomarchy.png diff --git a/themes/palettes/flexoki-light/apps/btop.theme b/themes/flexoki-light/btop.theme similarity index 100% rename from themes/palettes/flexoki-light/apps/btop.theme rename to themes/flexoki-light/btop.theme diff --git a/themes/gruvbox.json b/themes/gruvbox.json new file mode 100644 index 0000000..a95691d --- /dev/null +++ b/themes/gruvbox.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Gruvbox", + "slug": "gruvbox", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#282828", + "mantle": "#222222", + "surface": "#3c3836", + "overlay": "#3c3836", + "text": "#d4be98", + "subtext": "#d4be98", + "muted": "#3c3836", + "accent": "#7daea3", + "accentAlt": "#d3869b", + "good": "#a9b665", + "warn": "#d8a657", + "bad": "#ea6962" + }, + "ansi": [ + "#3c3836", + "#ea6962", + "#a9b665", + "#d8a657", + "#7daea3", + "#d3869b", + "#89b482", + "#d4be98", + "#3c3836", + "#ea6962", + "#a9b665", + "#d8a657", + "#7daea3", + "#d3869b", + "#89b482", + "#d4be98" + ] +} diff --git a/themes/palettes/gruvbox/backgrounds/1-the-backwater.jpg b/themes/gruvbox/backgrounds/1-the-backwater.jpg similarity index 100% rename from themes/palettes/gruvbox/backgrounds/1-the-backwater.jpg rename to themes/gruvbox/backgrounds/1-the-backwater.jpg diff --git a/themes/palettes/gruvbox/backgrounds/2-flower-basket.jpg b/themes/gruvbox/backgrounds/2-flower-basket.jpg similarity index 100% rename from themes/palettes/gruvbox/backgrounds/2-flower-basket.jpg rename to themes/gruvbox/backgrounds/2-flower-basket.jpg diff --git a/themes/palettes/gruvbox/backgrounds/3-village-square.jpg b/themes/gruvbox/backgrounds/3-village-square.jpg similarity index 100% rename from themes/palettes/gruvbox/backgrounds/3-village-square.jpg rename to themes/gruvbox/backgrounds/3-village-square.jpg diff --git a/themes/palettes/gruvbox/backgrounds/4-idyllic-procession.jpg b/themes/gruvbox/backgrounds/4-idyllic-procession.jpg similarity index 100% rename from themes/palettes/gruvbox/backgrounds/4-idyllic-procession.jpg rename to themes/gruvbox/backgrounds/4-idyllic-procession.jpg diff --git a/themes/palettes/gruvbox/backgrounds/5-leaves.jpg b/themes/gruvbox/backgrounds/5-leaves.jpg similarity index 100% rename from themes/palettes/gruvbox/backgrounds/5-leaves.jpg rename to themes/gruvbox/backgrounds/5-leaves.jpg diff --git a/themes/palettes/gruvbox/apps/btop.theme b/themes/gruvbox/btop.theme similarity index 100% rename from themes/palettes/gruvbox/apps/btop.theme rename to themes/gruvbox/btop.theme diff --git a/themes/hackerman.json b/themes/hackerman.json new file mode 100644 index 0000000..39bd534 --- /dev/null +++ b/themes/hackerman.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Hackerman", + "slug": "hackerman", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#0B0C16", + "mantle": "#090a13", + "surface": "#0B0C16", + "overlay": "#6a6e95", + "text": "#ddf7ff", + "subtext": "#85E1FB", + "muted": "#6a6e95", + "accent": "#82FB9C", + "accentAlt": "#86a7df", + "good": "#4fe88f", + "warn": "#50f7d4", + "bad": "#50f872" + }, + "ansi": [ + "#0B0C16", + "#50f872", + "#4fe88f", + "#50f7d4", + "#829dd4", + "#86a7df", + "#7cf8f7", + "#85E1FB", + "#6a6e95", + "#85ff9d", + "#9cf7c2", + "#a4ffec", + "#c4d2ed", + "#cddbf4", + "#d1fffe", + "#ddf7ff" + ] +} diff --git a/themes/palettes/hackerman/backgrounds/1-synth-scape.jpg b/themes/hackerman/backgrounds/1-synth-scape.jpg similarity index 100% rename from themes/palettes/hackerman/backgrounds/1-synth-scape.jpg rename to themes/hackerman/backgrounds/1-synth-scape.jpg diff --git a/themes/palettes/hackerman/backgrounds/2-geometric.jpg b/themes/hackerman/backgrounds/2-geometric.jpg similarity index 100% rename from themes/palettes/hackerman/backgrounds/2-geometric.jpg rename to themes/hackerman/backgrounds/2-geometric.jpg diff --git a/themes/palettes/hackerman/apps/btop.theme b/themes/hackerman/btop.theme similarity index 100% rename from themes/palettes/hackerman/apps/btop.theme rename to themes/hackerman/btop.theme diff --git a/themes/kanagawa.json b/themes/kanagawa.json new file mode 100644 index 0000000..96076d0 --- /dev/null +++ b/themes/kanagawa.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Kanagawa", + "slug": "kanagawa", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#1f1f28", + "mantle": "#1a1a22", + "surface": "#090618", + "overlay": "#727169", + "text": "#dcd7ba", + "subtext": "#c8c093", + "muted": "#727169", + "accent": "#7e9cd8", + "accentAlt": "#957fb8", + "good": "#76946a", + "warn": "#c0a36e", + "bad": "#c34043" + }, + "ansi": [ + "#090618", + "#c34043", + "#76946a", + "#c0a36e", + "#7e9cd8", + "#957fb8", + "#6a9589", + "#c8c093", + "#727169", + "#e82424", + "#98bb6c", + "#e6c384", + "#7fb4ca", + "#938aa9", + "#7aa89f", + "#dcd7ba" + ] +} diff --git a/themes/palettes/kanagawa/backgrounds/1-kanagawa.jpg b/themes/kanagawa/backgrounds/1-kanagawa.jpg similarity index 100% rename from themes/palettes/kanagawa/backgrounds/1-kanagawa.jpg rename to themes/kanagawa/backgrounds/1-kanagawa.jpg diff --git a/themes/palettes/kanagawa/apps/btop.theme b/themes/kanagawa/btop.theme similarity index 100% rename from themes/palettes/kanagawa/apps/btop.theme rename to themes/kanagawa/btop.theme diff --git a/themes/lumon.json b/themes/lumon.json new file mode 100644 index 0000000..e8558df --- /dev/null +++ b/themes/lumon.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Lumon", + "slug": "lumon", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#16242d", + "mantle": "#131f26", + "surface": "#1b2d40", + "overlay": "#304860", + "text": "#d6e2ee", + "subtext": "#d6e2ee", + "muted": "#304860", + "accent": "#f2fcff", + "accentAlt": "#8bc9eb", + "good": "#5e95bc", + "warn": "#6fa4c9", + "bad": "#4d86b0" + }, + "ansi": [ + "#1b2d40", + "#4d86b0", + "#5e95bc", + "#6fa4c9", + "#6fb8e3", + "#8bc9eb", + "#b4e4f6", + "#d6e2ee", + "#304860", + "#73a6cb", + "#86b7d8", + "#9dcae5", + "#f2fcff", + "#b1d8ee", + "#d1eef8", + "#ffffff" + ] +} diff --git a/themes/palettes/lumon/backgrounds/01-united-in-severance.jpg b/themes/lumon/backgrounds/01-united-in-severance.jpg similarity index 100% rename from themes/palettes/lumon/backgrounds/01-united-in-severance.jpg rename to themes/lumon/backgrounds/01-united-in-severance.jpg diff --git a/themes/palettes/lumon/backgrounds/02-opinions-equally.jpg b/themes/lumon/backgrounds/02-opinions-equally.jpg similarity index 100% rename from themes/palettes/lumon/backgrounds/02-opinions-equally.jpg rename to themes/lumon/backgrounds/02-opinions-equally.jpg diff --git a/themes/palettes/lumon/apps/btop.theme b/themes/lumon/btop.theme similarity index 100% rename from themes/palettes/lumon/apps/btop.theme rename to themes/lumon/btop.theme diff --git a/themes/lumon/waybar.css b/themes/lumon/waybar.css new file mode 100644 index 0000000..11f4905 --- /dev/null +++ b/themes/lumon/waybar.css @@ -0,0 +1,2 @@ +@define-color foreground #d6e2ee; +@define-color background #213442; diff --git a/themes/matte-black.json b/themes/matte-black.json new file mode 100644 index 0000000..714b5f2 --- /dev/null +++ b/themes/matte-black.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Matte Black", + "slug": "matte-black", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#121212", + "mantle": "#0f0f0f", + "surface": "#333333", + "overlay": "#8a8a8d", + "text": "#bebebe", + "subtext": "#bebebe", + "muted": "#8a8a8d", + "accent": "#e68e0d", + "accentAlt": "#D35F5F", + "good": "#FFC107", + "warn": "#b91c1c", + "bad": "#D35F5F" + }, + "ansi": [ + "#333333", + "#D35F5F", + "#FFC107", + "#b91c1c", + "#e68e0d", + "#D35F5F", + "#bebebe", + "#bebebe", + "#8a8a8d", + "#B91C1C", + "#FFC107", + "#b90a0a", + "#f59e0b", + "#B91C1C", + "#eaeaea", + "#ffffff" + ] +} diff --git a/themes/palettes/matte-black/backgrounds/0-ship-at-sea.jpg b/themes/matte-black/backgrounds/0-ship-at-sea.jpg similarity index 100% rename from themes/palettes/matte-black/backgrounds/0-ship-at-sea.jpg rename to themes/matte-black/backgrounds/0-ship-at-sea.jpg diff --git a/themes/palettes/matte-black/backgrounds/1-dark-waters.jpg b/themes/matte-black/backgrounds/1-dark-waters.jpg similarity index 100% rename from themes/palettes/matte-black/backgrounds/1-dark-waters.jpg rename to themes/matte-black/backgrounds/1-dark-waters.jpg diff --git a/themes/palettes/matte-black/backgrounds/2-dot-hands.jpg b/themes/matte-black/backgrounds/2-dot-hands.jpg similarity index 100% rename from themes/palettes/matte-black/backgrounds/2-dot-hands.jpg rename to themes/matte-black/backgrounds/2-dot-hands.jpg diff --git a/themes/palettes/matte-black/apps/btop.theme b/themes/matte-black/btop.theme similarity index 100% rename from themes/palettes/matte-black/apps/btop.theme rename to themes/matte-black/btop.theme diff --git a/themes/miasma.json b/themes/miasma.json new file mode 100644 index 0000000..bcc23cf --- /dev/null +++ b/themes/miasma.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Miasma", + "slug": "miasma", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#222222", + "mantle": "#1d1d1d", + "surface": "#000000", + "overlay": "#666666", + "text": "#c2c2b0", + "subtext": "#d7c483", + "muted": "#666666", + "accent": "#78824b", + "accentAlt": "#bb7744", + "good": "#5f875f", + "warn": "#b36d43", + "bad": "#685742" + }, + "ansi": [ + "#000000", + "#685742", + "#5f875f", + "#b36d43", + "#78824b", + "#bb7744", + "#c9a554", + "#d7c483", + "#666666", + "#685742", + "#5f875f", + "#b36d43", + "#78824b", + "#bb7744", + "#c9a554", + "#d7c483" + ] +} diff --git a/themes/palettes/miasma/backgrounds/01-nature-of-fear.jpg b/themes/miasma/backgrounds/01-nature-of-fear.jpg similarity index 100% rename from themes/palettes/miasma/backgrounds/01-nature-of-fear.jpg rename to themes/miasma/backgrounds/01-nature-of-fear.jpg diff --git a/themes/palettes/miasma/backgrounds/02-crowned.jpg b/themes/miasma/backgrounds/02-crowned.jpg similarity index 100% rename from themes/palettes/miasma/backgrounds/02-crowned.jpg rename to themes/miasma/backgrounds/02-crowned.jpg diff --git a/themes/palettes/miasma/apps/btop.theme b/themes/miasma/btop.theme similarity index 100% rename from themes/palettes/miasma/apps/btop.theme rename to themes/miasma/btop.theme diff --git a/themes/nord.json b/themes/nord.json new file mode 100644 index 0000000..6c12550 --- /dev/null +++ b/themes/nord.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Nord", + "slug": "nord", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#2e3440", + "mantle": "#272c36", + "surface": "#3b4252", + "overlay": "#4c566a", + "text": "#d8dee9", + "subtext": "#e5e9f0", + "muted": "#4c566a", + "accent": "#81a1c1", + "accentAlt": "#b48ead", + "good": "#a3be8c", + "warn": "#ebcb8b", + "bad": "#bf616a" + }, + "ansi": [ + "#3b4252", + "#bf616a", + "#a3be8c", + "#ebcb8b", + "#81a1c1", + "#b48ead", + "#88c0d0", + "#e5e9f0", + "#4c566a", + "#bf616a", + "#a3be8c", + "#ebcb8b", + "#81a1c1", + "#b48ead", + "#8fbcbb", + "#eceff4" + ] +} diff --git a/themes/palettes/nord/backgrounds/0-black-moon.jpg b/themes/nord/backgrounds/0-black-moon.jpg similarity index 100% rename from themes/palettes/nord/backgrounds/0-black-moon.jpg rename to themes/nord/backgrounds/0-black-moon.jpg diff --git a/themes/palettes/nord/backgrounds/1-city-view.png b/themes/nord/backgrounds/1-city-view.png similarity index 100% rename from themes/palettes/nord/backgrounds/1-city-view.png rename to themes/nord/backgrounds/1-city-view.png diff --git a/themes/palettes/nord/backgrounds/2-night-hawks.png b/themes/nord/backgrounds/2-night-hawks.png similarity index 100% rename from themes/palettes/nord/backgrounds/2-night-hawks.png rename to themes/nord/backgrounds/2-night-hawks.png diff --git a/themes/palettes/nord/apps/btop.theme b/themes/nord/btop.theme similarity index 100% rename from themes/palettes/nord/apps/btop.theme rename to themes/nord/btop.theme diff --git a/themes/nord/waybar.css b/themes/nord/waybar.css new file mode 100644 index 0000000..5238598 --- /dev/null +++ b/themes/nord/waybar.css @@ -0,0 +1,14 @@ +@define-color background #2e3440; +@define-color foreground #d8dee9; +@define-color accent #88c0d0; + +/* Base style for Nord */ +* { + font-family: JetBrainsMono Nerd Font, FontAwesome; + font-size: 13px; +} + +window#waybar { + background-color: @background; + color: @foreground; +} diff --git a/themes/osaka-jade.json b/themes/osaka-jade.json new file mode 100644 index 0000000..e794f56 --- /dev/null +++ b/themes/osaka-jade.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Osaka Jade", + "slug": "osaka-jade", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#111c18", + "mantle": "#0e1814", + "surface": "#23372B", + "overlay": "#53685B", + "text": "#C1C497", + "subtext": "#F6F5DD", + "muted": "#53685B", + "accent": "#509475", + "accentAlt": "#D2689C", + "good": "#549e6a", + "warn": "#459451", + "bad": "#FF5345" + }, + "ansi": [ + "#23372B", + "#FF5345", + "#549e6a", + "#459451", + "#509475", + "#D2689C", + "#2DD5B7", + "#F6F5DD", + "#53685B", + "#db9f9c", + "#63b07a", + "#E5C736", + "#ACD4CF", + "#75bbb3", + "#8CD3CB", + "#9eebb3" + ] +} diff --git a/themes/palettes/osaka-jade/backgrounds/1-glowing-city.jpg b/themes/osaka-jade/backgrounds/1-glowing-city.jpg similarity index 100% rename from themes/palettes/osaka-jade/backgrounds/1-glowing-city.jpg rename to themes/osaka-jade/backgrounds/1-glowing-city.jpg diff --git a/themes/palettes/osaka-jade/backgrounds/2-shaded-entrance.jpg b/themes/osaka-jade/backgrounds/2-shaded-entrance.jpg similarity index 100% rename from themes/palettes/osaka-jade/backgrounds/2-shaded-entrance.jpg rename to themes/osaka-jade/backgrounds/2-shaded-entrance.jpg diff --git a/themes/palettes/osaka-jade/backgrounds/3-mountain-moon.jpg b/themes/osaka-jade/backgrounds/3-mountain-moon.jpg similarity index 100% rename from themes/palettes/osaka-jade/backgrounds/3-mountain-moon.jpg rename to themes/osaka-jade/backgrounds/3-mountain-moon.jpg diff --git a/themes/palettes/osaka-jade/apps/btop.theme b/themes/osaka-jade/btop.theme similarity index 100% rename from themes/palettes/osaka-jade/apps/btop.theme rename to themes/osaka-jade/btop.theme diff --git a/themes/palettes/catppuccin-latte/apps/neovim.lua b/themes/palettes/catppuccin-latte/apps/neovim.lua deleted file mode 100644 index 53eaf45..0000000 --- a/themes/palettes/catppuccin-latte/apps/neovim.lua +++ /dev/null @@ -1,16 +0,0 @@ -return { - { - "catppuccin/nvim", - name = "catppuccin", - priority = 1000, - opts = { - flavour = "latte", - }, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "catppuccin-latte", - }, - }, -} diff --git a/themes/palettes/catppuccin-latte/apps/vscode.json b/themes/palettes/catppuccin-latte/apps/vscode.json deleted file mode 100644 index 98d1113..0000000 --- a/themes/palettes/catppuccin-latte/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Catppuccin Latte", - "extension": "catppuccin.catppuccin-vsc" -} diff --git a/themes/palettes/catppuccin-latte/colors.toml b/themes/palettes/catppuccin-latte/colors.toml deleted file mode 100644 index 87c37d4..0000000 --- a/themes/palettes/catppuccin-latte/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#1e66f5" -cursor = "#dc8a78" -foreground = "#4c4f69" -background = "#eff1f5" -selection_foreground = "#eff1f5" -selection_background = "#dc8a78" - -color0 = "#bcc0cc" -color1 = "#d20f39" -color2 = "#40a02b" -color3 = "#df8e1d" -color4 = "#1e66f5" -color5 = "#ea76cb" -color6 = "#179299" -color7 = "#5c5f77" -color8 = "#acb0be" -color9 = "#d20f39" -color10 = "#40a02b" -color11 = "#df8e1d" -color12 = "#1e66f5" -color13 = "#ea76cb" -color14 = "#179299" -color15 = "#6c6f85" diff --git a/themes/palettes/catppuccin-latte/icons.theme b/themes/palettes/catppuccin-latte/icons.theme deleted file mode 100644 index 6ce2f14..0000000 --- a/themes/palettes/catppuccin-latte/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue diff --git a/themes/palettes/catppuccin-latte/light.mode b/themes/palettes/catppuccin-latte/light.mode deleted file mode 100644 index 66bb2d0..0000000 --- a/themes/palettes/catppuccin-latte/light.mode +++ /dev/null @@ -1 +0,0 @@ -# This will set "prefer-light" and use "Adwaita" as the theme diff --git a/themes/palettes/catppuccin-latte/preview.png b/themes/palettes/catppuccin-latte/preview.png deleted file mode 100644 index d3ef70e..0000000 Binary files a/themes/palettes/catppuccin-latte/preview.png and /dev/null differ diff --git a/themes/palettes/catppuccin/apps/neovim.lua b/themes/palettes/catppuccin/apps/neovim.lua deleted file mode 100644 index a868387..0000000 --- a/themes/palettes/catppuccin/apps/neovim.lua +++ /dev/null @@ -1,13 +0,0 @@ -return { - { - "catppuccin/nvim", - name = "catppuccin", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "catppuccin", - }, - }, -} diff --git a/themes/palettes/catppuccin/apps/vscode.json b/themes/palettes/catppuccin/apps/vscode.json deleted file mode 100644 index a94d2cf..0000000 --- a/themes/palettes/catppuccin/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Catppuccin Mocha", - "extension": "catppuccin.catppuccin-vsc" -} diff --git a/themes/palettes/catppuccin/colors.toml b/themes/palettes/catppuccin/colors.toml deleted file mode 100644 index 6943ef1..0000000 --- a/themes/palettes/catppuccin/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#89b4fa" -cursor = "#f5e0dc" -foreground = "#cdd6f4" -background = "#1e1e2e" -selection_foreground = "#1e1e2e" -selection_background = "#f5e0dc" - -color0 = "#45475a" -color1 = "#f38ba8" -color2 = "#a6e3a1" -color3 = "#f9e2af" -color4 = "#89b4fa" -color5 = "#f5c2e7" -color6 = "#94e2d5" -color7 = "#bac2de" -color8 = "#585b70" -color9 = "#f38ba8" -color10 = "#a6e3a1" -color11 = "#f9e2af" -color12 = "#89b4fa" -color13 = "#f5c2e7" -color14 = "#94e2d5" -color15 = "#a6adc8" diff --git a/themes/palettes/catppuccin/icons.theme b/themes/palettes/catppuccin/icons.theme deleted file mode 100644 index 24a4551..0000000 --- a/themes/palettes/catppuccin/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-purple diff --git a/themes/palettes/catppuccin/preview.png b/themes/palettes/catppuccin/preview.png deleted file mode 100644 index cf75f13..0000000 Binary files a/themes/palettes/catppuccin/preview.png and /dev/null differ diff --git a/themes/palettes/default.nix b/themes/palettes/default.nix deleted file mode 100644 index 255f787..0000000 --- a/themes/palettes/default.nix +++ /dev/null @@ -1,44 +0,0 @@ -let - themesDir = ./.; - - # Get all directories in the themes folder that have a colors.toml file - allEntries = builtins.readDir themesDir; - directories = builtins.filter (name: - allEntries.${name} == "directory" && builtins.pathExists (themesDir + "/${name}/colors.toml") - ) (builtins.attrNames allEntries); - - readTheme = name: - let - toml = builtins.fromTOML (builtins.readFile (themesDir + "/${name}/colors.toml")); - - # Helper to strip '#' from color codes - stripHash = s: builtins.replaceStrings ["#"] [""] s; - - in { - inherit name; - author = "nomarchy"; - palette = { - base00 = stripHash toml.background; - base01 = stripHash toml.color0; - base02 = stripHash toml.color8; - base03 = stripHash toml.color8; - base04 = stripHash toml.color7; - base05 = stripHash toml.foreground; - base06 = stripHash toml.color15; - base07 = stripHash toml.color15; - base08 = stripHash toml.color1; - base09 = stripHash toml.color3; - base0A = stripHash toml.color3; - base0B = stripHash toml.color2; - base0C = stripHash toml.color6; - base0D = stripHash toml.color4; - base0E = stripHash toml.color5; - base0F = stripHash toml.color1; - }; - }; - -in - builtins.listToAttrs (map (name: { - inherit name; - value = readTheme name; - }) directories) diff --git a/themes/palettes/ethereal/apps/neovim.lua b/themes/palettes/ethereal/apps/neovim.lua deleted file mode 100644 index 885bece..0000000 --- a/themes/palettes/ethereal/apps/neovim.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - "bjarneo/ethereal.nvim", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "ethereal", - }, - }, -} diff --git a/themes/palettes/ethereal/apps/vscode.json b/themes/palettes/ethereal/apps/vscode.json deleted file mode 100644 index 253a33d..0000000 --- a/themes/palettes/ethereal/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Ethereal", - "extension": "Bjarne.ethereal-nomarchy" -} diff --git a/themes/palettes/ethereal/colors.toml b/themes/palettes/ethereal/colors.toml deleted file mode 100644 index 75cedec..0000000 --- a/themes/palettes/ethereal/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#7d82d9" -cursor = "#ffcead" -foreground = "#ffcead" -background = "#060B1E" -selection_foreground = "#060B1E" -selection_background = "#ffcead" - -color0 = "#060B1E" -color1 = "#ED5B5A" -color2 = "#92a593" -color3 = "#E9BB4F" -color4 = "#7d82d9" -color5 = "#c89dc1" -color6 = "#a3bfd1" -color7 = "#F99957" -color8 = "#6d7db6" -color9 = "#faaaa9" -color10 = "#c4cfc4" -color11 = "#f7dc9c" -color12 = "#c2c4f0" -color13 = "#ead7e7" -color14 = "#dfeaf0" -color15 = "#ffcead" diff --git a/themes/palettes/ethereal/icons.theme b/themes/palettes/ethereal/icons.theme deleted file mode 100644 index 6ce2f14..0000000 --- a/themes/palettes/ethereal/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue diff --git a/themes/palettes/ethereal/preview.png b/themes/palettes/ethereal/preview.png deleted file mode 100644 index b9b413c..0000000 Binary files a/themes/palettes/ethereal/preview.png and /dev/null differ diff --git a/themes/palettes/everforest/apps/neovim.lua b/themes/palettes/everforest/apps/neovim.lua deleted file mode 100644 index 80551c7..0000000 --- a/themes/palettes/everforest/apps/neovim.lua +++ /dev/null @@ -1,10 +0,0 @@ -return { - { "neanias/everforest-nvim" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "everforest", - background = "soft", - }, - }, -} diff --git a/themes/palettes/everforest/apps/vscode.json b/themes/palettes/everforest/apps/vscode.json deleted file mode 100644 index 02b107d..0000000 --- a/themes/palettes/everforest/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Everforest Dark", - "extension": "sainnhe.everforest" -} diff --git a/themes/palettes/everforest/colors.toml b/themes/palettes/everforest/colors.toml deleted file mode 100644 index 9aaeeb5..0000000 --- a/themes/palettes/everforest/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#7fbbb3" -cursor = "#d3c6aa" -foreground = "#d3c6aa" -background = "#2d353b" -selection_foreground = "#2d353b" -selection_background = "#d3c6aa" - -color0 = "#475258" -color1 = "#e67e80" -color2 = "#a7c080" -color3 = "#dbbc7f" -color4 = "#7fbbb3" -color5 = "#d699b6" -color6 = "#83c092" -color7 = "#d3c6aa" -color8 = "#475258" -color9 = "#e67e80" -color10 = "#a7c080" -color11 = "#dbbc7f" -color12 = "#7fbbb3" -color13 = "#d699b6" -color14 = "#83c092" -color15 = "#d3c6aa" diff --git a/themes/palettes/everforest/icons.theme b/themes/palettes/everforest/icons.theme deleted file mode 100644 index 140e422..0000000 --- a/themes/palettes/everforest/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-sage diff --git a/themes/palettes/everforest/preview.png b/themes/palettes/everforest/preview.png deleted file mode 100644 index f328fa5..0000000 Binary files a/themes/palettes/everforest/preview.png and /dev/null differ diff --git a/themes/palettes/flexoki-light/apps/neovim.lua b/themes/palettes/flexoki-light/apps/neovim.lua deleted file mode 100644 index 230c551..0000000 --- a/themes/palettes/flexoki-light/apps/neovim.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - "kepano/flexoki-neovim", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "flexoki-light", - }, - }, -} diff --git a/themes/palettes/flexoki-light/apps/vscode.json b/themes/palettes/flexoki-light/apps/vscode.json deleted file mode 100644 index 00a2045..0000000 --- a/themes/palettes/flexoki-light/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "flexoki-light", - "extension": "shadesOfBuntu.flexoki-light" -} diff --git a/themes/palettes/flexoki-light/colors.toml b/themes/palettes/flexoki-light/colors.toml deleted file mode 100644 index 44af88c..0000000 --- a/themes/palettes/flexoki-light/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#205EA6" -cursor = "#100F0F" -foreground = "#100F0F" -background = "#FFFCF0" -selection_foreground = "#100F0F" -selection_background = "#CECDC3" - -color0 = "#100F0F" -color1 = "#D14D41" -color2 = "#879A39" -color3 = "#D0A215" -color4 = "#205EA6" -color5 = "#CE5D97" -color6 = "#3AA99F" -color7 = "#FFFCF0" -color8 = "#100F0F" -color9 = "#D14D41" -color10 = "#879A39" -color11 = "#D0A215" -color12 = "#4385BE" -color13 = "#CE5D97" -color14 = "#3AA99F" -color15 = "#FFFCF0" diff --git a/themes/palettes/flexoki-light/icons.theme b/themes/palettes/flexoki-light/icons.theme deleted file mode 100644 index 6ce2f14..0000000 --- a/themes/palettes/flexoki-light/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue diff --git a/themes/palettes/flexoki-light/light.mode b/themes/palettes/flexoki-light/light.mode deleted file mode 100644 index e65461b..0000000 --- a/themes/palettes/flexoki-light/light.mode +++ /dev/null @@ -1 +0,0 @@ -# This will set "prefer-light" and use "Adwaita" as the theme \ No newline at end of file diff --git a/themes/palettes/flexoki-light/preview.png b/themes/palettes/flexoki-light/preview.png deleted file mode 100644 index 3a350a2..0000000 Binary files a/themes/palettes/flexoki-light/preview.png and /dev/null differ diff --git a/themes/palettes/gruvbox/apps/neovim.lua b/themes/palettes/gruvbox/apps/neovim.lua deleted file mode 100644 index edaf0d5..0000000 --- a/themes/palettes/gruvbox/apps/neovim.lua +++ /dev/null @@ -1,9 +0,0 @@ -return { - { "ellisonleao/gruvbox.nvim" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "gruvbox", - }, - }, -} diff --git a/themes/palettes/gruvbox/apps/vscode.json b/themes/palettes/gruvbox/apps/vscode.json deleted file mode 100644 index e75a9b9..0000000 --- a/themes/palettes/gruvbox/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Gruvbox Dark Medium", - "extension": "jdinhlife.gruvbox" -} diff --git a/themes/palettes/gruvbox/colors.toml b/themes/palettes/gruvbox/colors.toml deleted file mode 100644 index 57847f0..0000000 --- a/themes/palettes/gruvbox/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#7daea3" -cursor = "#bdae93" -foreground = "#d4be98" -background = "#282828" -selection_foreground = "#ebdbb2" -selection_background = "#d65d0e" - -color0 = "#3c3836" -color1 = "#ea6962" -color2 = "#a9b665" -color3 = "#d8a657" -color4 = "#7daea3" -color5 = "#d3869b" -color6 = "#89b482" -color7 = "#d4be98" -color8 = "#3c3836" -color9 = "#ea6962" -color10 = "#a9b665" -color11 = "#d8a657" -color12 = "#7daea3" -color13 = "#d3869b" -color14 = "#89b482" -color15 = "#d4be98" diff --git a/themes/palettes/gruvbox/icons.theme b/themes/palettes/gruvbox/icons.theme deleted file mode 100644 index 7bb20fc..0000000 --- a/themes/palettes/gruvbox/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-olive diff --git a/themes/palettes/gruvbox/preview.png b/themes/palettes/gruvbox/preview.png deleted file mode 100644 index 1287fa2..0000000 Binary files a/themes/palettes/gruvbox/preview.png and /dev/null differ diff --git a/themes/palettes/hackerman/apps/neovim.lua b/themes/palettes/hackerman/apps/neovim.lua deleted file mode 100644 index d4588f0..0000000 --- a/themes/palettes/hackerman/apps/neovim.lua +++ /dev/null @@ -1,13 +0,0 @@ -return { - { - "bjarneo/hackerman.nvim", - dependencies = { "bjarneo/aether.nvim" }, -- Ensure aether is loaded first - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "hackerman", - }, - }, -} diff --git a/themes/palettes/hackerman/apps/vscode.json b/themes/palettes/hackerman/apps/vscode.json deleted file mode 100644 index dc56bb1..0000000 --- a/themes/palettes/hackerman/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Hackerman", - "extension": "Bjarne.hackerman-nomarchy" -} diff --git a/themes/palettes/hackerman/colors.toml b/themes/palettes/hackerman/colors.toml deleted file mode 100644 index 56aec2a..0000000 --- a/themes/palettes/hackerman/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#82FB9C" -cursor = "#ddf7ff" -foreground = "#ddf7ff" -background = "#0B0C16" -selection_foreground = "#0B0C16" -selection_background = "#ddf7ff" - -color0 = "#0B0C16" -color1 = "#50f872" -color2 = "#4fe88f" -color3 = "#50f7d4" -color4 = "#829dd4" -color5 = "#86a7df" -color6 = "#7cf8f7" -color7 = "#85E1FB" -color8 = "#6a6e95" -color9 = "#85ff9d" -color10 = "#9cf7c2" -color11 = "#a4ffec" -color12 = "#c4d2ed" -color13 = "#cddbf4" -color14 = "#d1fffe" -color15 = "#ddf7ff" diff --git a/themes/palettes/hackerman/icons.theme b/themes/palettes/hackerman/icons.theme deleted file mode 100644 index 66be38a..0000000 --- a/themes/palettes/hackerman/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue \ No newline at end of file diff --git a/themes/palettes/hackerman/preview.png b/themes/palettes/hackerman/preview.png deleted file mode 100644 index 2d4c0c3..0000000 Binary files a/themes/palettes/hackerman/preview.png and /dev/null differ diff --git a/themes/palettes/kanagawa/apps/neovim.lua b/themes/palettes/kanagawa/apps/neovim.lua deleted file mode 100644 index b31e9e5..0000000 --- a/themes/palettes/kanagawa/apps/neovim.lua +++ /dev/null @@ -1,9 +0,0 @@ -return { - { "rebelot/kanagawa.nvim" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "kanagawa", - }, - }, -} diff --git a/themes/palettes/kanagawa/apps/vscode.json b/themes/palettes/kanagawa/apps/vscode.json deleted file mode 100644 index 91f7539..0000000 --- a/themes/palettes/kanagawa/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Kanagawa", - "extension": "qufiwefefwoyn.kanagawa" -} diff --git a/themes/palettes/kanagawa/colors.toml b/themes/palettes/kanagawa/colors.toml deleted file mode 100644 index 7ca002e..0000000 --- a/themes/palettes/kanagawa/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#7e9cd8" -cursor = "#c8c093" -foreground = "#dcd7ba" -background = "#1f1f28" -selection_foreground = "#c8c093" -selection_background = "#2d4f67" - -color0 = "#090618" -color1 = "#c34043" -color2 = "#76946a" -color3 = "#c0a36e" -color4 = "#7e9cd8" -color5 = "#957fb8" -color6 = "#6a9589" -color7 = "#c8c093" -color8 = "#727169" -color9 = "#e82424" -color10 = "#98bb6c" -color11 = "#e6c384" -color12 = "#7fb4ca" -color13 = "#938aa9" -color14 = "#7aa89f" -color15 = "#dcd7ba" diff --git a/themes/palettes/kanagawa/icons.theme b/themes/palettes/kanagawa/icons.theme deleted file mode 100644 index 6ce2f14..0000000 --- a/themes/palettes/kanagawa/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue diff --git a/themes/palettes/kanagawa/preview.png b/themes/palettes/kanagawa/preview.png deleted file mode 100644 index 67b1ac1..0000000 Binary files a/themes/palettes/kanagawa/preview.png and /dev/null differ diff --git a/themes/palettes/lumon/apps/neovim.lua b/themes/palettes/lumon/apps/neovim.lua deleted file mode 100644 index 0c6407c..0000000 --- a/themes/palettes/lumon/apps/neovim.lua +++ /dev/null @@ -1,13 +0,0 @@ -return { - { - "omacom-io/lumon.nvim", - name = "lumon", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "lumon", - }, - }, -} diff --git a/themes/palettes/lumon/apps/swayosd.css b/themes/palettes/lumon/apps/swayosd.css deleted file mode 100644 index 5d71e32..0000000 --- a/themes/palettes/lumon/apps/swayosd.css +++ /dev/null @@ -1,44 +0,0 @@ -@define-color background-color #1b2d40; -@define-color border-color #304860; -@define-color label #d6e2ee; -@define-color image #d6e2ee; -@define-color progress #6fb8e3; -@define-color edge-light #f2fcff; - -/* Cancel out Nomarchy defaults */ -window:not(:backdrop), -window:backdrop { - border: none; - border-width: 0; - background-color: transparent; - box-shadow: none; - padding: 12px; -} - -/* Draw the Lumon OSD shell */ -window:not(:backdrop) #container, -window:backdrop #container { - border: 2px solid alpha(@border-color, 0.92); - background-color: alpha(@background-color, 0.95); - padding: 12px 16px; - background-clip: padding-box; -} - -image, -label { - color: @label; -} - -progressbar { - min-height: 8px; -} - -progressbar trough { - background: alpha(@border-color, 0.24); - box-shadow: inset 0 1px rgba(242, 252, 255, 0.03); -} - -progressbar progress { - background: linear-gradient(90deg, @progress, @edge-light); - box-shadow: 0 0 10px rgba(111, 184, 227, 0.18); -} diff --git a/themes/palettes/lumon/apps/vscode.json b/themes/palettes/lumon/apps/vscode.json deleted file mode 100644 index 045878d..0000000 --- a/themes/palettes/lumon/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Lumon", - "extension": "oldjobobo.lumon-theme" -} diff --git a/themes/palettes/lumon/colors.toml b/themes/palettes/lumon/colors.toml deleted file mode 100644 index b94e758..0000000 --- a/themes/palettes/lumon/colors.toml +++ /dev/null @@ -1,35 +0,0 @@ -# Accent and UI colors -accent = "#f2fcff" -active_border_color = "#f2fcff" -active_tab_background = "#6fb8e3" - -# Cursor colors -cursor = "#f2fcff" - -# Primary colors -foreground = "#d6e2ee" -background = "#16242d" - -# Selection colors -selection_foreground = "#1b2d40" -selection_background = "#4d9ed3" - -# Normal colors (ANSI 0-7) -color0 = "#1b2d40" -color1 = "#4d86b0" -color2 = "#5e95bc" -color3 = "#6fa4c9" -color4 = "#6fb8e3" -color5 = "#8bc9eb" -color6 = "#b4e4f6" -color7 = "#d6e2ee" - -# Bright colors (ANSI 8-15) -color8 = "#304860" -color9 = "#73a6cb" -color10 = "#86b7d8" -color11 = "#9dcae5" -color12 = "#f2fcff" -color13 = "#b1d8ee" -color14 = "#d1eef8" -color15 = "#ffffff" diff --git a/themes/palettes/lumon/icons.theme b/themes/palettes/lumon/icons.theme deleted file mode 100644 index 66be38a..0000000 --- a/themes/palettes/lumon/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue \ No newline at end of file diff --git a/themes/palettes/lumon/preview.png b/themes/palettes/lumon/preview.png deleted file mode 100644 index 9a87f6d..0000000 Binary files a/themes/palettes/lumon/preview.png and /dev/null differ diff --git a/themes/palettes/matte-black/apps/neovim.lua b/themes/palettes/matte-black/apps/neovim.lua deleted file mode 100644 index 7b3f72b..0000000 --- a/themes/palettes/matte-black/apps/neovim.lua +++ /dev/null @@ -1,9 +0,0 @@ -return { - { "tahayvr/matteblack.nvim", lazy = false, priority = 1000 }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "matteblack", - }, - }, -} \ No newline at end of file diff --git a/themes/palettes/matte-black/apps/vscode.json b/themes/palettes/matte-black/apps/vscode.json deleted file mode 100644 index 7786fc7..0000000 --- a/themes/palettes/matte-black/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Matte Black", - "extension": "TahaYVR.matteblack" -} diff --git a/themes/palettes/matte-black/colors.toml b/themes/palettes/matte-black/colors.toml deleted file mode 100644 index f0f1d3a..0000000 --- a/themes/palettes/matte-black/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#e68e0d" -cursor = "#eaeaea" -foreground = "#bebebe" -background = "#121212" -selection_foreground = "#bebebe" -selection_background = "#333333" - -color0 = "#333333" -color1 = "#D35F5F" -color2 = "#FFC107" -color3 = "#b91c1c" -color4 = "#e68e0d" -color5 = "#D35F5F" -color6 = "#bebebe" -color7 = "#bebebe" -color8 = "#8a8a8d" -color9 = "#B91C1C" -color10 = "#FFC107" -color11 = "#b90a0a" -color12 = "#f59e0b" -color13 = "#B91C1C" -color14 = "#eaeaea" -color15 = "#ffffff" diff --git a/themes/palettes/matte-black/icons.theme b/themes/palettes/matte-black/icons.theme deleted file mode 100644 index a3c0a4c..0000000 --- a/themes/palettes/matte-black/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-red diff --git a/themes/palettes/matte-black/preview.png b/themes/palettes/matte-black/preview.png deleted file mode 100644 index ed9c7b4..0000000 Binary files a/themes/palettes/matte-black/preview.png and /dev/null differ diff --git a/themes/palettes/miasma/apps/neovim.lua b/themes/palettes/miasma/apps/neovim.lua deleted file mode 100644 index a46c690..0000000 --- a/themes/palettes/miasma/apps/neovim.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - "OldJobobo/miasma.nvim", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "miasma", - }, - }, -} diff --git a/themes/palettes/miasma/apps/vscode.json b/themes/palettes/miasma/apps/vscode.json deleted file mode 100644 index f133d52..0000000 --- a/themes/palettes/miasma/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Miasma", - "extension": "oldjobobo.miasma-theme" -} diff --git a/themes/palettes/miasma/colors.toml b/themes/palettes/miasma/colors.toml deleted file mode 100644 index 705ce2e..0000000 --- a/themes/palettes/miasma/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#78824b" -cursor = "#c7c7c7" -foreground = "#c2c2b0" -background = "#222222" -selection_foreground = "#c2c2b0" -selection_background = "#78824b" - -color0 = "#000000" -color1 = "#685742" -color2 = "#5f875f" -color3 = "#b36d43" -color4 = "#78824b" -color5 = "#bb7744" -color6 = "#c9a554" -color7 = "#d7c483" -color8 = "#666666" -color9 = "#685742" -color10 = "#5f875f" -color11 = "#b36d43" -color12 = "#78824b" -color13 = "#bb7744" -color14 = "#c9a554" -color15 = "#d7c483" diff --git a/themes/palettes/miasma/icons.theme b/themes/palettes/miasma/icons.theme deleted file mode 100644 index 37b2350..0000000 --- a/themes/palettes/miasma/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-wartybrown \ No newline at end of file diff --git a/themes/palettes/miasma/preview.png b/themes/palettes/miasma/preview.png deleted file mode 100644 index e080d82..0000000 Binary files a/themes/palettes/miasma/preview.png and /dev/null differ diff --git a/themes/palettes/nord/apps/neovim.lua b/themes/palettes/nord/apps/neovim.lua deleted file mode 100644 index 27a68b1..0000000 --- a/themes/palettes/nord/apps/neovim.lua +++ /dev/null @@ -1,9 +0,0 @@ -return { - { "EdenEast/nightfox.nvim" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "nordfox", - }, - }, -} diff --git a/themes/palettes/nord/apps/vscode.json b/themes/palettes/nord/apps/vscode.json deleted file mode 100644 index 897b8ba..0000000 --- a/themes/palettes/nord/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Nord", - "extension": "arcticicestudio.nord-visual-studio-code" -} diff --git a/themes/palettes/nord/colors.toml b/themes/palettes/nord/colors.toml deleted file mode 100644 index 79c2ae2..0000000 --- a/themes/palettes/nord/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#81a1c1" -cursor = "#d8dee9" -foreground = "#d8dee9" -background = "#2e3440" -selection_foreground = "#d8dee9" -selection_background = "#4c566a" - -color0 = "#3b4252" -color1 = "#bf616a" -color2 = "#a3be8c" -color3 = "#ebcb8b" -color4 = "#81a1c1" -color5 = "#b48ead" -color6 = "#88c0d0" -color7 = "#e5e9f0" -color8 = "#4c566a" -color9 = "#bf616a" -color10 = "#a3be8c" -color11 = "#ebcb8b" -color12 = "#81a1c1" -color13 = "#b48ead" -color14 = "#8fbcbb" -color15 = "#eceff4" diff --git a/themes/palettes/nord/icons.theme b/themes/palettes/nord/icons.theme deleted file mode 100644 index 6ce2f14..0000000 --- a/themes/palettes/nord/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue diff --git a/themes/palettes/nord/preview.png b/themes/palettes/nord/preview.png deleted file mode 100644 index 57d5035..0000000 Binary files a/themes/palettes/nord/preview.png and /dev/null differ diff --git a/themes/palettes/osaka-jade/apps/neovim.lua b/themes/palettes/osaka-jade/apps/neovim.lua deleted file mode 100644 index 30afe40..0000000 --- a/themes/palettes/osaka-jade/apps/neovim.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - "ribru17/bamboo.nvim", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "bamboo", - }, - }, -} diff --git a/themes/palettes/osaka-jade/apps/vscode.json b/themes/palettes/osaka-jade/apps/vscode.json deleted file mode 100644 index efe2d63..0000000 --- a/themes/palettes/osaka-jade/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Ocean Green: Dark", - "extension": "jovejonovski.ocean-green" -} diff --git a/themes/palettes/osaka-jade/colors.toml b/themes/palettes/osaka-jade/colors.toml deleted file mode 100644 index 76f763e..0000000 --- a/themes/palettes/osaka-jade/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#509475" -cursor = "#D7C995" -foreground = "#C1C497" -background = "#111c18" -selection_foreground = "#111C18" -selection_background = "#C1C497" - -color0 = "#23372B" -color1 = "#FF5345" -color2 = "#549e6a" -color3 = "#459451" -color4 = "#509475" -color5 = "#D2689C" -color6 = "#2DD5B7" -color7 = "#F6F5DD" -color8 = "#53685B" -color9 = "#db9f9c" -color10 = "#63b07a" -color11 = "#E5C736" -color12 = "#ACD4CF" -color13 = "#75bbb3" -color14 = "#8CD3CB" -color15 = "#9eebb3" diff --git a/themes/palettes/osaka-jade/icons.theme b/themes/palettes/osaka-jade/icons.theme deleted file mode 100644 index 140e422..0000000 --- a/themes/palettes/osaka-jade/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-sage diff --git a/themes/palettes/osaka-jade/preview.png b/themes/palettes/osaka-jade/preview.png deleted file mode 100644 index c1fe99f..0000000 Binary files a/themes/palettes/osaka-jade/preview.png and /dev/null differ diff --git a/themes/palettes/retro-82/apps/neovim.lua b/themes/palettes/retro-82/apps/neovim.lua deleted file mode 100644 index 871a218..0000000 --- a/themes/palettes/retro-82/apps/neovim.lua +++ /dev/null @@ -1,13 +0,0 @@ -return { - { - "OldJobobo/retro-82.nvim", - name = "retro-82", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "retro-82", - }, - }, -} diff --git a/themes/palettes/retro-82/apps/swayosd.css b/themes/palettes/retro-82/apps/swayosd.css deleted file mode 100644 index b3e2364..0000000 --- a/themes/palettes/retro-82/apps/swayosd.css +++ /dev/null @@ -1,5 +0,0 @@ -@define-color background-color #00172e; -@define-color border-color #134e5a; -@define-color label #f6dcac; -@define-color image #f6dcac; -@define-color progress #e97b3c; diff --git a/themes/palettes/retro-82/apps/vscode.json b/themes/palettes/retro-82/apps/vscode.json deleted file mode 100644 index e2b3bbd..0000000 --- a/themes/palettes/retro-82/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Retro '82", - "extension": "oldjobobo.retro-82-theme" -} diff --git a/themes/palettes/retro-82/colors.toml b/themes/palettes/retro-82/colors.toml deleted file mode 100644 index 21cf810..0000000 --- a/themes/palettes/retro-82/colors.toml +++ /dev/null @@ -1,35 +0,0 @@ -# Accent and UI colors -accent = "#faa968" -active_border_color = "#faa968" -active_tab_background = "#faa968" - -# Cursor colors -cursor = "#f6dcac" - -# Primary colors -foreground = "#f6dcac" -background = "#05182e" - -# Selection colors -selection_foreground = "#00172e" -selection_background = "#faa968" - -# Normal colors (ANSI 0-7) -color0 = "#00172e" -color1 = "#f85525" -color2 = "#028391" -color3 = "#e97b3c" -color4 = "#faa968" -color5 = "#3f8f8a" -color6 = "#8cbfb8" -color7 = "#a7c9c6" - -# Bright colors (ANSI 8-15) -color8 = "#134e5a" -color9 = "#f85525" -color10 = "#028391" -color11 = "#e97b3c" -color12 = "#faa968" -color13 = "#3f8f8a" -color14 = "#8cbfb8" -color15 = "#f6dcac" diff --git a/themes/palettes/retro-82/icons.theme b/themes/palettes/retro-82/icons.theme deleted file mode 100644 index 3a73239..0000000 --- a/themes/palettes/retro-82/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-wartybrown diff --git a/themes/palettes/retro-82/preview.png b/themes/palettes/retro-82/preview.png deleted file mode 100644 index 328ae9f..0000000 Binary files a/themes/palettes/retro-82/preview.png and /dev/null differ diff --git a/themes/palettes/ristretto/apps/neovim.lua b/themes/palettes/ristretto/apps/neovim.lua deleted file mode 100644 index 4263da7..0000000 --- a/themes/palettes/ristretto/apps/neovim.lua +++ /dev/null @@ -1,31 +0,0 @@ -return { - { - "gthelding/monokai-pro.nvim", - config = function() - require("monokai-pro").setup({ - filter = "ristretto", - override = function() - return { - NonText = { fg = "#948a8b" }, - MiniIconsGrey = { fg = "#948a8b" }, - MiniIconsRed = { fg = "#fd6883" }, - MiniIconsBlue = { fg = "#85dacc" }, - MiniIconsGreen = { fg = "#adda78" }, - MiniIconsYellow = { fg = "#f9cc6c" }, - MiniIconsOrange = { fg = "#f38d70" }, - MiniIconsPurple = { fg = "#a8a9eb" }, - MiniIconsAzure = { fg = "#a8a9eb" }, - MiniIconsCyan = { fg = "#85dacc" }, -- same value as MiniIconsBlue for consistency - } - end, - }) - vim.cmd([[colorscheme monokai-pro]]) - end, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "monokai-pro", - }, - }, -} diff --git a/themes/palettes/ristretto/apps/vscode.json b/themes/palettes/ristretto/apps/vscode.json deleted file mode 100644 index 41a587e..0000000 --- a/themes/palettes/ristretto/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Monokai Pro (Filter Ristretto)", - "extension": "monokai.theme-monokai-pro-vscode" -} diff --git a/themes/palettes/ristretto/colors.toml b/themes/palettes/ristretto/colors.toml deleted file mode 100644 index b5ca9ab..0000000 --- a/themes/palettes/ristretto/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#f38d70" -cursor = "#c3b7b8" -foreground = "#e6d9db" -background = "#2c2525" -selection_foreground = "#e6d9db" -selection_background = "#403e41" - -color0 = "#72696a" -color1 = "#fd6883" -color2 = "#adda78" -color3 = "#f9cc6c" -color4 = "#f38d70" -color5 = "#a8a9eb" -color6 = "#85dacc" -color7 = "#e6d9db" -color8 = "#948a8b" -color9 = "#ff8297" -color10 = "#c8e292" -color11 = "#fcd675" -color12 = "#f8a788" -color13 = "#bebffd" -color14 = "#9bf1e1" -color15 = "#f1e5e7" diff --git a/themes/palettes/ristretto/icons.theme b/themes/palettes/ristretto/icons.theme deleted file mode 100644 index e38b9ce..0000000 --- a/themes/palettes/ristretto/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-yellow diff --git a/themes/palettes/ristretto/preview.png b/themes/palettes/ristretto/preview.png deleted file mode 100644 index 978b80f..0000000 Binary files a/themes/palettes/ristretto/preview.png and /dev/null differ diff --git a/themes/palettes/rose-pine/apps/neovim.lua b/themes/palettes/rose-pine/apps/neovim.lua deleted file mode 100644 index 591e8c7..0000000 --- a/themes/palettes/rose-pine/apps/neovim.lua +++ /dev/null @@ -1,9 +0,0 @@ -return { - { "rose-pine/neovim", name = "rose-pine" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "rose-pine-dawn", - }, - }, -} diff --git a/themes/palettes/rose-pine/apps/vscode.json b/themes/palettes/rose-pine/apps/vscode.json deleted file mode 100644 index 07e0050..0000000 --- a/themes/palettes/rose-pine/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Rosé Pine Dawn", - "extension": "mvllow.rose-pine" -} diff --git a/themes/palettes/rose-pine/colors.toml b/themes/palettes/rose-pine/colors.toml deleted file mode 100644 index e757ad0..0000000 --- a/themes/palettes/rose-pine/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#56949f" -cursor = "#cecacd" -foreground = "#575279" -background = "#faf4ed" -selection_foreground = "#575279" -selection_background = "#dfdad9" - -color0 = "#f2e9e1" -color1 = "#b4637a" -color2 = "#286983" -color3 = "#ea9d34" -color4 = "#56949f" -color5 = "#907aa9" -color6 = "#d7827e" -color7 = "#575279" -color8 = "#9893a5" -color9 = "#b4637a" -color10 = "#286983" -color11 = "#ea9d34" -color12 = "#56949f" -color13 = "#907aa9" -color14 = "#d7827e" -color15 = "#575279" diff --git a/themes/palettes/rose-pine/icons.theme b/themes/palettes/rose-pine/icons.theme deleted file mode 100644 index 6ce2f14..0000000 --- a/themes/palettes/rose-pine/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue diff --git a/themes/palettes/rose-pine/light.mode b/themes/palettes/rose-pine/light.mode deleted file mode 100644 index 66bb2d0..0000000 --- a/themes/palettes/rose-pine/light.mode +++ /dev/null @@ -1 +0,0 @@ -# This will set "prefer-light" and use "Adwaita" as the theme diff --git a/themes/palettes/rose-pine/preview.png b/themes/palettes/rose-pine/preview.png deleted file mode 100644 index e5fa45a..0000000 Binary files a/themes/palettes/rose-pine/preview.png and /dev/null differ diff --git a/themes/palettes/summer-day/apps/neovim.lua b/themes/palettes/summer-day/apps/neovim.lua deleted file mode 100644 index cea322f..0000000 --- a/themes/palettes/summer-day/apps/neovim.lua +++ /dev/null @@ -1,16 +0,0 @@ -return { - { "neanias/everforest-nvim" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "everforest", - background = "soft", - }, - }, - { - "LazyVim/LazyVim", - init = function() - vim.opt.background = "light" - end, - }, -} diff --git a/themes/palettes/summer-day/apps/rofi.rasi b/themes/palettes/summer-day/apps/rofi.rasi deleted file mode 100644 index c86b299..0000000 --- a/themes/palettes/summer-day/apps/rofi.rasi +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Nomarchy Summer Day Rofi Theme - * Converted from everforest.rasi and config - */ - -configuration { - modi: "drun"; - show-icons: true; - icon-theme: "Papirus-Light"; - terminal: "alacritty"; - drun-display-format: "{name}"; - case-sensitive: false; - location: 0; - disable-history: false; - hide-scrollbar: true; - display-drun: "Search"; -} - -* { - /* Summer Day color definitions */ - bg0: #fdf6e3; - fg: #5c6a72; - red: #f85552; - yellow: #dfa000; - green: #8da101; - aqua: #35a77c; - blue: #3a94c5; - purple: #df69ba; - grey0: #a6b0a0; - - font: "Iosevka Nerd Font 17"; - background-color: transparent; - text-color: @fg; -} - -window { - transparency: "real"; - background-color: @fg; - text-color: @bg0; - border-bottom: 5px; - border-color: @yellow; - border-radius: 15px; - width: 40%; - height: 50%; - location: center; - anchor: center; - x-offset: 0; - y-offset: 0; -} - -mainbox { - background-color: transparent; - children: [ inputbar, listview ]; - spacing: 0px; - padding: 0px; -} - -inputbar { - children: [ prompt, entry ]; - background-color: @green; - text-color: @bg0; - expand: false; - border-bottom: 5px; - border-color: @grey0; - border-radius: 10px; - margin: 15px; - padding: 10px; - spacing: 15px; -} - -prompt { - enabled: true; - background-color: inherit; - text-color: inherit; -} - -entry { - background-color: inherit; - text-color: inherit; - cursor: text; - placeholder: "Type to search..."; - placeholder-color: @bg0; -} - -listview { - background-color: transparent; - columns: 1; - lines: 8; - spacing: 0px; - cycle: true; - dynamic: true; - layout: vertical; - padding: 20px; -} - -element { - background-color: transparent; - text-color: @bg0; - orientation: horizontal; - border-radius: 10px; - padding: 10px; - spacing: 15px; -} - -element-icon { - background-color: transparent; - text-color: inherit; - size: 40px; - cursor: inherit; -} - -element-text { - background-color: transparent; - text-color: inherit; - highlight: bold; - cursor: inherit; - vertical-align: 0.5; - horizontal-align: 0.0; -} - -element selected { - background-color: @bg0; - text-color: @fg; - border-bottom: 5px; - border-color: #bdc3af; - border-radius: 10px; -} - -element-text selected { - text-color: @fg; -} - -element-icon selected { - text-color: @fg; -} - -scrollbar { - width: 0px; - background-color: transparent; - handle-width: 0px; - handle-color: transparent; - border: 0px; -} diff --git a/themes/palettes/summer-day/apps/vscode.json b/themes/palettes/summer-day/apps/vscode.json deleted file mode 100644 index 04b7a3f..0000000 --- a/themes/palettes/summer-day/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Everforest Light", - "extension": "sainnhe.everforest" -} diff --git a/themes/palettes/summer-day/colors.toml b/themes/palettes/summer-day/colors.toml deleted file mode 100644 index 6ab9cc1..0000000 --- a/themes/palettes/summer-day/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#3a94c5" -cursor = "#5c6a72" -foreground = "#5c6a72" -background = "#fdf6e3" -selection_foreground = "#5c6a72" -selection_background = "#d3dbc8" - -color0 = "#5c6a72" -color1 = "#f85552" -color2 = "#8da101" -color3 = "#dfa000" -color4 = "#3a94c5" -color5 = "#df69ba" -color6 = "#35a77c" -color7 = "#fdf6e3" -color8 = "#829181" -color9 = "#f85552" -color10 = "#8da101" -color11 = "#dfa000" -color12 = "#3a94c5" -color13 = "#df69ba" -color14 = "#35a77c" -color15 = "#fdf6e3" diff --git a/themes/palettes/summer-day/icons.theme b/themes/palettes/summer-day/icons.theme deleted file mode 100644 index 6ce2f14..0000000 --- a/themes/palettes/summer-day/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-blue diff --git a/themes/palettes/summer-day/light.mode b/themes/palettes/summer-day/light.mode deleted file mode 100644 index 66bb2d0..0000000 --- a/themes/palettes/summer-day/light.mode +++ /dev/null @@ -1 +0,0 @@ -# This will set "prefer-light" and use "Adwaita" as the theme diff --git a/themes/palettes/summer-night/apps/neovim.lua b/themes/palettes/summer-night/apps/neovim.lua deleted file mode 100644 index 80551c7..0000000 --- a/themes/palettes/summer-night/apps/neovim.lua +++ /dev/null @@ -1,10 +0,0 @@ -return { - { "neanias/everforest-nvim" }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "everforest", - background = "soft", - }, - }, -} diff --git a/themes/palettes/summer-night/apps/rofi.rasi b/themes/palettes/summer-night/apps/rofi.rasi deleted file mode 100644 index bf804cd..0000000 --- a/themes/palettes/summer-night/apps/rofi.rasi +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Nomarchy Summer Night Rofi Theme - * Converted from everforest.rasi and config - */ - -configuration { - modi: "drun"; - show-icons: true; - icon-theme: "Papirus-Dark"; - terminal: "alacritty"; - drun-display-format: "{name}"; - case-sensitive: false; - location: 0; - disable-history: false; - hide-scrollbar: true; - display-drun: "Search"; -} - -* { - /* Summer Night color definitions */ - bg0: #2d353b; - fg: #d3c6aa; - red: #e68183; - yellow: #d9bb80; - green: #a7c080; - aqua: #83b6af; - blue: #83b6af; - purple: #d39bb6; - grey0: #868d80; - - font: "Iosevka Nerd Font 17"; - background-color: transparent; - text-color: @fg; -} - -window { - transparency: "real"; - background-color: @fg; - text-color: @bg0; - border-bottom: 5px; - border-color: @yellow; - border-radius: 15px; - width: 40%; - height: 50%; - location: center; - anchor: center; - x-offset: 0; - y-offset: 0; -} - -mainbox { - background-color: transparent; - children: [ inputbar, listview ]; - spacing: 0px; - padding: 0px; -} - -inputbar { - children: [ prompt, entry ]; - background-color: @green; - text-color: @bg0; - expand: false; - border-bottom: 5px; - border-color: @grey0; - border-radius: 10px; - margin: 15px; - padding: 10px; - spacing: 15px; -} - -prompt { - enabled: true; - background-color: inherit; - text-color: inherit; -} - -entry { - background-color: inherit; - text-color: inherit; - cursor: text; - placeholder: "Type to search..."; - placeholder-color: @bg0; -} - -listview { - background-color: transparent; - columns: 1; - lines: 8; - spacing: 0px; - cycle: true; - dynamic: true; - layout: vertical; - padding: 20px; -} - -element { - background-color: transparent; - text-color: @bg0; - orientation: horizontal; - border-radius: 10px; - padding: 10px; - spacing: 15px; -} - -element-icon { - background-color: transparent; - text-color: inherit; - size: 40px; - cursor: inherit; -} - -element-text { - background-color: transparent; - text-color: inherit; - highlight: bold; - cursor: inherit; - vertical-align: 0.5; - horizontal-align: 0.0; -} - -element selected { - background-color: @bg0; - text-color: @fg; - border-bottom: 5px; - border-color: #161a1d; - border-radius: 10px; -} - -element-text selected { - text-color: @fg; -} - -element-icon selected { - text-color: @fg; -} - -scrollbar { - width: 0px; - background-color: transparent; - handle-width: 0px; - handle-color: transparent; - border: 0px; -} diff --git a/themes/palettes/summer-night/apps/vscode.json b/themes/palettes/summer-night/apps/vscode.json deleted file mode 100644 index 02b107d..0000000 --- a/themes/palettes/summer-night/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Everforest Dark", - "extension": "sainnhe.everforest" -} diff --git a/themes/palettes/summer-night/colors.toml b/themes/palettes/summer-night/colors.toml deleted file mode 100644 index 89bb7d9..0000000 --- a/themes/palettes/summer-night/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#83b6af" -cursor = "#d3c6aa" -foreground = "#d3c6aa" -background = "#2d353b" -selection_foreground = "#d3c6aa" -selection_background = "#505a60" - -color0 = "#3c474d" -color1 = "#e68183" -color2 = "#a7c080" -color3 = "#d9bb80" -color4 = "#83b6af" -color5 = "#d39bb6" -color6 = "#87c095" -color7 = "#868d80" -color8 = "#868d80" -color9 = "#e68183" -color10 = "#a7c080" -color11 = "#d9bb80" -color12 = "#83b6af" -color13 = "#d39bb6" -color14 = "#87c095" -color15 = "#868d80" diff --git a/themes/palettes/summer-night/icons.theme b/themes/palettes/summer-night/icons.theme deleted file mode 100644 index 771ed6b..0000000 --- a/themes/palettes/summer-night/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Everforest-Dark diff --git a/themes/palettes/tokyo-night/apps/keyboard.rgb b/themes/palettes/tokyo-night/apps/keyboard.rgb deleted file mode 100644 index c9f4a7c..0000000 --- a/themes/palettes/tokyo-night/apps/keyboard.rgb +++ /dev/null @@ -1 +0,0 @@ -ff00ff diff --git a/themes/palettes/tokyo-night/apps/neovim.lua b/themes/palettes/tokyo-night/apps/neovim.lua deleted file mode 100644 index 39327c9..0000000 --- a/themes/palettes/tokyo-night/apps/neovim.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - "folke/tokyonight.nvim", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "tokyonight-night", - }, - }, -} diff --git a/themes/palettes/tokyo-night/apps/vscode.json b/themes/palettes/tokyo-night/apps/vscode.json deleted file mode 100644 index 89af98c..0000000 --- a/themes/palettes/tokyo-night/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Tokyo Night", - "extension": "enkia.tokyo-night" -} diff --git a/themes/palettes/tokyo-night/colors.toml b/themes/palettes/tokyo-night/colors.toml deleted file mode 100644 index fe51dd0..0000000 --- a/themes/palettes/tokyo-night/colors.toml +++ /dev/null @@ -1,23 +0,0 @@ -accent = "#7aa2f7" -cursor = "#c0caf5" -foreground = "#a9b1d6" -background = "#1a1b26" -selection_foreground = "#c0caf5" -selection_background = "#7aa2f7" - -color0 = "#32344a" -color1 = "#f7768e" -color2 = "#9ece6a" -color3 = "#e0af68" -color4 = "#7aa2f7" -color5 = "#ad8ee6" -color6 = "#449dab" -color7 = "#787c99" -color8 = "#444b6a" -color9 = "#ff7a93" -color10 = "#b9f27c" -color11 = "#ff9e64" -color12 = "#7da6ff" -color13 = "#bb9af7" -color14 = "#0db9d7" -color15 = "#acb0d0" diff --git a/themes/palettes/tokyo-night/icons.theme b/themes/palettes/tokyo-night/icons.theme deleted file mode 100644 index 5d00638..0000000 --- a/themes/palettes/tokyo-night/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-magenta diff --git a/themes/palettes/tokyo-night/preview.png b/themes/palettes/tokyo-night/preview.png deleted file mode 100644 index 8f5f7e3..0000000 Binary files a/themes/palettes/tokyo-night/preview.png and /dev/null differ diff --git a/themes/palettes/vantablack/apps/neovim.lua b/themes/palettes/vantablack/apps/neovim.lua deleted file mode 100644 index f9d9616..0000000 --- a/themes/palettes/vantablack/apps/neovim.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - "bjarneo/vantablack.nvim", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "vantablack", - }, - }, -} diff --git a/themes/palettes/vantablack/apps/vscode.json b/themes/palettes/vantablack/apps/vscode.json deleted file mode 100644 index a4edfca..0000000 --- a/themes/palettes/vantablack/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Vantablack", - "extension": "Bjarne.vantablack-nomarchy" -} diff --git a/themes/palettes/vantablack/colors.toml b/themes/palettes/vantablack/colors.toml deleted file mode 100644 index 96e095f..0000000 --- a/themes/palettes/vantablack/colors.toml +++ /dev/null @@ -1,31 +0,0 @@ -# UI Colors (extended) -accent = "#8d8d8d" -cursor = "#ffffff" - -# Primary colors -foreground = "#ffffff" -background = "#0d0d0d" - -# Selection colors -selection_foreground = "#0d0d0d" -selection_background = "#ffffff" - -# Normal colors (ANSI 0-7) -color0 = "#0d0d0d" -color1 = "#a4a4a4" -color2 = "#b6b6b6" -color3 = "#cecece" -color4 = "#8d8d8d" -color5 = "#9b9b9b" -color6 = "#b0b0b0" -color7 = "#ececec" - -# Bright colors (ANSI 8-15) -color8 = "#fdfdfd" -color9 = "#a4a4a4" -color10 = "#b6b6b6" -color11 = "#cecece" -color12 = "#8d8d8d" -color13 = "#9b9b9b" -color14 = "#b0b0b0" -color15 = "#ffffff" diff --git a/themes/palettes/vantablack/icons.theme b/themes/palettes/vantablack/icons.theme deleted file mode 100644 index 0bc3e2d..0000000 --- a/themes/palettes/vantablack/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-gray diff --git a/themes/palettes/vantablack/preview.png b/themes/palettes/vantablack/preview.png deleted file mode 100644 index e6a3a72..0000000 Binary files a/themes/palettes/vantablack/preview.png and /dev/null differ diff --git a/themes/palettes/white/apps/neovim.lua b/themes/palettes/white/apps/neovim.lua deleted file mode 100644 index f9afa38..0000000 --- a/themes/palettes/white/apps/neovim.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - "bjarneo/white.nvim", - priority = 1000, - }, - { - "LazyVim/LazyVim", - opts = { - colorscheme = "white", - }, - }, -} diff --git a/themes/palettes/white/apps/vscode.json b/themes/palettes/white/apps/vscode.json deleted file mode 100644 index 70019ac..0000000 --- a/themes/palettes/white/apps/vscode.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "White", - "extension": "Bjarne.white-theme" -} diff --git a/themes/palettes/white/colors.toml b/themes/palettes/white/colors.toml deleted file mode 100644 index de6cce9..0000000 --- a/themes/palettes/white/colors.toml +++ /dev/null @@ -1,31 +0,0 @@ -# UI Colors (extended) -accent = "#6e6e6e" -cursor = "#000000" - -# Primary colors -foreground = "#000000" -background = "#ffffff" - -# Selection colors -selection_foreground = "#ffffff" -selection_background = "#1a1a1a" - -# Normal colors (ANSI 0-7) -color0 = "#ffffff" -color1 = "#2a2a2a" -color2 = "#3a3a3a" -color3 = "#4a4a4a" -color4 = "#1a1a1a" -color5 = "#2e2e2e" -color6 = "#3e3e3e" -color7 = "#000000" - -# Bright colors (ANSI 8-15) -color8 = "#c0c0c0" -color9 = "#2a2a2a" -color10 = "#3a3a3a" -color11 = "#4a4a4a" -color12 = "#1a1a1a" -color13 = "#2e2e2e" -color14 = "#3e3e3e" -color15 = "#000000" diff --git a/themes/palettes/white/icons.theme b/themes/palettes/white/icons.theme deleted file mode 100644 index 3d157d9..0000000 --- a/themes/palettes/white/icons.theme +++ /dev/null @@ -1 +0,0 @@ -Yaru-grey diff --git a/themes/palettes/white/light.mode b/themes/palettes/white/light.mode deleted file mode 100644 index 66bb2d0..0000000 --- a/themes/palettes/white/light.mode +++ /dev/null @@ -1 +0,0 @@ -# This will set "prefer-light" and use "Adwaita" as the theme diff --git a/themes/palettes/white/preview.png b/themes/palettes/white/preview.png deleted file mode 100644 index 597b3c4..0000000 Binary files a/themes/palettes/white/preview.png and /dev/null differ diff --git a/themes/retro-82.json b/themes/retro-82.json new file mode 100644 index 0000000..a5c9177 --- /dev/null +++ b/themes/retro-82.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Retro 82", + "slug": "retro-82", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#05182e", + "mantle": "#041427", + "surface": "#00172e", + "overlay": "#134e5a", + "text": "#f6dcac", + "subtext": "#a7c9c6", + "muted": "#134e5a", + "accent": "#faa968", + "accentAlt": "#3f8f8a", + "good": "#028391", + "warn": "#e97b3c", + "bad": "#f85525" + }, + "ansi": [ + "#00172e", + "#f85525", + "#028391", + "#e97b3c", + "#faa968", + "#3f8f8a", + "#8cbfb8", + "#a7c9c6", + "#134e5a", + "#f85525", + "#028391", + "#e97b3c", + "#faa968", + "#3f8f8a", + "#8cbfb8", + "#f6dcac" + ] +} diff --git a/themes/palettes/retro-82/backgrounds/1-in-the-groove.jpg b/themes/retro-82/backgrounds/1-in-the-groove.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/1-in-the-groove.jpg rename to themes/retro-82/backgrounds/1-in-the-groove.jpg diff --git a/themes/palettes/retro-82/backgrounds/2-dusk-guardian.jpg b/themes/retro-82/backgrounds/2-dusk-guardian.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/2-dusk-guardian.jpg rename to themes/retro-82/backgrounds/2-dusk-guardian.jpg diff --git a/themes/palettes/retro-82/backgrounds/3-glassy-lines.jpg b/themes/retro-82/backgrounds/3-glassy-lines.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/3-glassy-lines.jpg rename to themes/retro-82/backgrounds/3-glassy-lines.jpg diff --git a/themes/palettes/retro-82/backgrounds/4-gateway.jpg b/themes/retro-82/backgrounds/4-gateway.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/4-gateway.jpg rename to themes/retro-82/backgrounds/4-gateway.jpg diff --git a/themes/palettes/retro-82/backgrounds/5-zen-boat.jpg b/themes/retro-82/backgrounds/5-zen-boat.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/5-zen-boat.jpg rename to themes/retro-82/backgrounds/5-zen-boat.jpg diff --git a/themes/palettes/retro-82/backgrounds/6-abstract-pyramids.jpg b/themes/retro-82/backgrounds/6-abstract-pyramids.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/6-abstract-pyramids.jpg rename to themes/retro-82/backgrounds/6-abstract-pyramids.jpg diff --git a/themes/palettes/retro-82/backgrounds/7-the-journey.jpg b/themes/retro-82/backgrounds/7-the-journey.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/7-the-journey.jpg rename to themes/retro-82/backgrounds/7-the-journey.jpg diff --git a/themes/palettes/retro-82/backgrounds/8-glitter-glass.jpg b/themes/retro-82/backgrounds/8-glitter-glass.jpg similarity index 100% rename from themes/palettes/retro-82/backgrounds/8-glitter-glass.jpg rename to themes/retro-82/backgrounds/8-glitter-glass.jpg diff --git a/themes/palettes/retro-82/apps/btop.theme b/themes/retro-82/btop.theme similarity index 100% rename from themes/palettes/retro-82/apps/btop.theme rename to themes/retro-82/btop.theme diff --git a/themes/retro-82/waybar.css b/themes/retro-82/waybar.css new file mode 100644 index 0000000..f5944b1 --- /dev/null +++ b/themes/retro-82/waybar.css @@ -0,0 +1,3 @@ +@define-color bg #00172e; +@define-color foreground #f6dcac; +@define-color background alpha(@bg, 0.8); diff --git a/themes/ristretto.json b/themes/ristretto.json new file mode 100644 index 0000000..528d5aa --- /dev/null +++ b/themes/ristretto.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Ristretto", + "slug": "ristretto", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#2c2525", + "mantle": "#251f1f", + "surface": "#72696a", + "overlay": "#948a8b", + "text": "#e6d9db", + "subtext": "#e6d9db", + "muted": "#948a8b", + "accent": "#f38d70", + "accentAlt": "#a8a9eb", + "good": "#adda78", + "warn": "#f9cc6c", + "bad": "#fd6883" + }, + "ansi": [ + "#72696a", + "#fd6883", + "#adda78", + "#f9cc6c", + "#f38d70", + "#a8a9eb", + "#85dacc", + "#e6d9db", + "#948a8b", + "#ff8297", + "#c8e292", + "#fcd675", + "#f8a788", + "#bebffd", + "#9bf1e1", + "#f1e5e7" + ] +} diff --git a/themes/palettes/ristretto/backgrounds/1-color-curves.jpg b/themes/ristretto/backgrounds/1-color-curves.jpg similarity index 100% rename from themes/palettes/ristretto/backgrounds/1-color-curves.jpg rename to themes/ristretto/backgrounds/1-color-curves.jpg diff --git a/themes/palettes/ristretto/backgrounds/2-coffee-beans.jpg b/themes/ristretto/backgrounds/2-coffee-beans.jpg similarity index 100% rename from themes/palettes/ristretto/backgrounds/2-coffee-beans.jpg rename to themes/ristretto/backgrounds/2-coffee-beans.jpg diff --git a/themes/palettes/ristretto/backgrounds/3-industrial-moon.jpg b/themes/ristretto/backgrounds/3-industrial-moon.jpg similarity index 100% rename from themes/palettes/ristretto/backgrounds/3-industrial-moon.jpg rename to themes/ristretto/backgrounds/3-industrial-moon.jpg diff --git a/themes/palettes/ristretto/apps/btop.theme b/themes/ristretto/btop.theme similarity index 100% rename from themes/palettes/ristretto/apps/btop.theme rename to themes/ristretto/btop.theme diff --git a/themes/rose-pine.json b/themes/rose-pine.json new file mode 100644 index 0000000..b11da41 --- /dev/null +++ b/themes/rose-pine.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Rose Pine", + "slug": "rose-pine", + "mode": "light", + "wallpaper": "", + "colors": { + "base": "#faf4ed", + "mantle": "#d4cfc9", + "surface": "#f2e9e1", + "overlay": "#9893a5", + "text": "#575279", + "subtext": "#575279", + "muted": "#9893a5", + "accent": "#56949f", + "accentAlt": "#907aa9", + "good": "#286983", + "warn": "#ea9d34", + "bad": "#b4637a" + }, + "ansi": [ + "#f2e9e1", + "#b4637a", + "#286983", + "#ea9d34", + "#56949f", + "#907aa9", + "#d7827e", + "#575279", + "#9893a5", + "#b4637a", + "#286983", + "#ea9d34", + "#56949f", + "#907aa9", + "#d7827e", + "#575279" + ] +} diff --git a/themes/palettes/rose-pine/backgrounds/1-funky-shapes.jpg b/themes/rose-pine/backgrounds/1-funky-shapes.jpg similarity index 100% rename from themes/palettes/rose-pine/backgrounds/1-funky-shapes.jpg rename to themes/rose-pine/backgrounds/1-funky-shapes.jpg diff --git a/themes/palettes/rose-pine/backgrounds/2-dot-map.png b/themes/rose-pine/backgrounds/2-dot-map.png similarity index 100% rename from themes/palettes/rose-pine/backgrounds/2-dot-map.png rename to themes/rose-pine/backgrounds/2-dot-map.png diff --git a/themes/palettes/rose-pine/backgrounds/3-nomarchy-plants.png b/themes/rose-pine/backgrounds/3-nomarchy-plants.png similarity index 100% rename from themes/palettes/rose-pine/backgrounds/3-nomarchy-plants.png rename to themes/rose-pine/backgrounds/3-nomarchy-plants.png diff --git a/themes/palettes/rose-pine/apps/btop.theme b/themes/rose-pine/btop.theme similarity index 100% rename from themes/palettes/rose-pine/apps/btop.theme rename to themes/rose-pine/btop.theme diff --git a/themes/summer-day.json b/themes/summer-day.json new file mode 100644 index 0000000..29132ad --- /dev/null +++ b/themes/summer-day.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Summer Day", + "slug": "summer-day", + "mode": "light", + "wallpaper": "", + "colors": { + "base": "#fdf6e3", + "mantle": "#d7d1c1", + "surface": "#5c6a72", + "overlay": "#829181", + "text": "#5c6a72", + "subtext": "#fdf6e3", + "muted": "#829181", + "accent": "#3a94c5", + "accentAlt": "#df69ba", + "good": "#8da101", + "warn": "#dfa000", + "bad": "#f85552" + }, + "ansi": [ + "#5c6a72", + "#f85552", + "#8da101", + "#dfa000", + "#3a94c5", + "#df69ba", + "#35a77c", + "#fdf6e3", + "#829181", + "#f85552", + "#8da101", + "#dfa000", + "#3a94c5", + "#df69ba", + "#35a77c", + "#fdf6e3" + ] +} diff --git a/themes/palettes/summer-day/backgrounds/1-summer-day.png b/themes/summer-day/backgrounds/1-summer-day.png similarity index 100% rename from themes/palettes/summer-day/backgrounds/1-summer-day.png rename to themes/summer-day/backgrounds/1-summer-day.png diff --git a/themes/palettes/summer-day/apps/btop.theme b/themes/summer-day/btop.theme similarity index 100% rename from themes/palettes/summer-day/apps/btop.theme rename to themes/summer-day/btop.theme diff --git a/features/desktop/waybar/themes/summer-day/style.css b/themes/summer-day/waybar.css similarity index 100% rename from features/desktop/waybar/themes/summer-day/style.css rename to themes/summer-day/waybar.css diff --git a/themes/summer-night.json b/themes/summer-night.json new file mode 100644 index 0000000..488d3b1 --- /dev/null +++ b/themes/summer-night.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Summer Night", + "slug": "summer-night", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#2d353b", + "mantle": "#262d32", + "surface": "#3c474d", + "overlay": "#868d80", + "text": "#d3c6aa", + "subtext": "#868d80", + "muted": "#868d80", + "accent": "#83b6af", + "accentAlt": "#d39bb6", + "good": "#a7c080", + "warn": "#d9bb80", + "bad": "#e68183" + }, + "ansi": [ + "#3c474d", + "#e68183", + "#a7c080", + "#d9bb80", + "#83b6af", + "#d39bb6", + "#87c095", + "#868d80", + "#868d80", + "#e68183", + "#a7c080", + "#d9bb80", + "#83b6af", + "#d39bb6", + "#87c095", + "#868d80" + ] +} diff --git a/themes/palettes/summer-night/backgrounds/1-summer-night.png b/themes/summer-night/backgrounds/1-summer-night.png similarity index 100% rename from themes/palettes/summer-night/backgrounds/1-summer-night.png rename to themes/summer-night/backgrounds/1-summer-night.png diff --git a/themes/palettes/summer-night/apps/btop.theme b/themes/summer-night/btop.theme similarity index 100% rename from themes/palettes/summer-night/apps/btop.theme rename to themes/summer-night/btop.theme diff --git a/features/desktop/waybar/themes/summer-night/style.css b/themes/summer-night/waybar.css similarity index 100% rename from features/desktop/waybar/themes/summer-night/style.css rename to themes/summer-night/waybar.css diff --git a/themes/templates/keyboard.rgb.tpl b/themes/templates/keyboard.rgb.tpl deleted file mode 100644 index bc9f35a..0000000 --- a/themes/templates/keyboard.rgb.tpl +++ /dev/null @@ -1 +0,0 @@ -{{ accent }} diff --git a/themes/templates/obsidian.css.tpl b/themes/templates/obsidian.css.tpl deleted file mode 100644 index 7645a50..0000000 --- a/themes/templates/obsidian.css.tpl +++ /dev/null @@ -1,99 +0,0 @@ -/* Nomarchy Theme for Obsidian */ - -.theme-dark, .theme-light { - /* Core colors */ - --background-primary: {{ background }}; - --background-primary-alt: {{ background }}; - --background-secondary: {{ background }}; - --background-secondary-alt: {{ background }}; - --text-normal: {{ foreground }}; - - /* Selection colors */ - --text-selection: {{ selection_background }}; - - /* Border color */ - --background-modifier-border: {{ color8 }}; - - /* Semantic heading colors */ - --text-title-h1: {{ color1 }}; - --text-title-h2: {{ color2 }}; - --text-title-h3: {{ color3 }}; - --text-title-h4: {{ color4 }}; - --text-title-h5: {{ color5 }}; - --text-title-h6: {{ color5 }}; - - /* Links and accents */ - --text-link: {{ color4 }}; - --text-accent: {{ accent }}; - --text-accent-hover: {{ accent }}; - --interactive-accent: {{ accent }}; - --interactive-accent-hover: {{ accent }}; - - /* Muted text */ - --text-muted: color-mix(in srgb, {{ foreground }} 70%, transparent); - --text-faint: color-mix(in srgb, {{ foreground }} 55%, transparent); - - /* Code */ - --code-normal: {{ color6 }}; - - /* Errors and success */ - --text-error: {{ color1 }}; - --text-error-hover: {{ color1 }}; - --text-success: {{ color2 }}; - - /* Tags */ - --tag-color: {{ color6 }}; - --tag-background: {{ color8 }}; - - /* Graph */ - --graph-line: {{ color8 }}; - --graph-node: {{ accent }}; - --graph-node-focused: {{ color4 }}; - --graph-node-tag: {{ color6 }}; - --graph-node-attachment: {{ color2 }}; -} - -/* Headers */ -.cm-header-1, .markdown-rendered h1 { color: var(--text-title-h1); } -.cm-header-2, .markdown-rendered h2 { color: var(--text-title-h2); } -.cm-header-3, .markdown-rendered h3 { color: var(--text-title-h3); } -.cm-header-4, .markdown-rendered h4 { color: var(--text-title-h4); } -.cm-header-5, .markdown-rendered h5 { color: var(--text-title-h5); } -.cm-header-6, .markdown-rendered h6 { color: var(--text-title-h6); } - -/* Code blocks */ -.markdown-rendered code { - color: {{ color6 }}; -} - -/* Syntax highlighting */ -.cm-s-obsidian span.cm-keyword { color: {{ color1 }}; } -.cm-s-obsidian span.cm-string { color: {{ color2 }}; } -.cm-s-obsidian span.cm-number { color: {{ color3 }}; } -.cm-s-obsidian span.cm-comment { color: {{ color8 }}; } -.cm-s-obsidian span.cm-operator { color: {{ color4 }}; } -.cm-s-obsidian span.cm-def { color: {{ color4 }}; } - -/* Links */ -.markdown-rendered a { - color: var(--text-link); -} - -/* Blockquotes */ -.markdown-rendered blockquote { - border-left-color: {{ accent }}; -} - -/* Active elements */ -.workspace-leaf.mod-active .workspace-leaf-header-title { - color: var(--interactive-accent); -} - -.nav-file-title.is-active { - color: var(--interactive-accent); -} - -/* Search results */ -.search-result-file-title { - color: var(--interactive-accent); -} diff --git a/themes/tokyo-night.json b/themes/tokyo-night.json new file mode 100644 index 0000000..aa7b209 --- /dev/null +++ b/themes/tokyo-night.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Tokyo Night", + "slug": "tokyo-night", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#1a1b26", + "mantle": "#161720", + "surface": "#32344a", + "overlay": "#444b6a", + "text": "#a9b1d6", + "subtext": "#787c99", + "muted": "#444b6a", + "accent": "#7aa2f7", + "accentAlt": "#ad8ee6", + "good": "#9ece6a", + "warn": "#e0af68", + "bad": "#f7768e" + }, + "ansi": [ + "#32344a", + "#f7768e", + "#9ece6a", + "#e0af68", + "#7aa2f7", + "#ad8ee6", + "#449dab", + "#787c99", + "#444b6a", + "#ff7a93", + "#b9f27c", + "#ff9e64", + "#7da6ff", + "#bb9af7", + "#0db9d7", + "#acb0d0" + ] +} diff --git a/themes/palettes/tokyo-night/backgrounds/0-swirl-buck.jpg b/themes/tokyo-night/backgrounds/0-swirl-buck.jpg similarity index 100% rename from themes/palettes/tokyo-night/backgrounds/0-swirl-buck.jpg rename to themes/tokyo-night/backgrounds/0-swirl-buck.jpg diff --git a/themes/palettes/tokyo-night/backgrounds/1-sunset-lake.png b/themes/tokyo-night/backgrounds/1-sunset-lake.png similarity index 100% rename from themes/palettes/tokyo-night/backgrounds/1-sunset-lake.png rename to themes/tokyo-night/backgrounds/1-sunset-lake.png diff --git a/themes/palettes/tokyo-night/backgrounds/2-pawel-czerwinski.jpg b/themes/tokyo-night/backgrounds/2-pawel-czerwinski.jpg similarity index 100% rename from themes/palettes/tokyo-night/backgrounds/2-pawel-czerwinski.jpg rename to themes/tokyo-night/backgrounds/2-pawel-czerwinski.jpg diff --git a/themes/palettes/tokyo-night/backgrounds/3-milad-fakurian.jpg b/themes/tokyo-night/backgrounds/3-milad-fakurian.jpg similarity index 100% rename from themes/palettes/tokyo-night/backgrounds/3-milad-fakurian.jpg rename to themes/tokyo-night/backgrounds/3-milad-fakurian.jpg diff --git a/themes/palettes/tokyo-night/apps/btop.theme b/themes/tokyo-night/btop.theme similarity index 100% rename from themes/palettes/tokyo-night/apps/btop.theme rename to themes/tokyo-night/btop.theme diff --git a/themes/vantablack.json b/themes/vantablack.json new file mode 100644 index 0000000..fada473 --- /dev/null +++ b/themes/vantablack.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "Vantablack", + "slug": "vantablack", + "mode": "dark", + "wallpaper": "", + "colors": { + "base": "#0d0d0d", + "mantle": "#0b0b0b", + "surface": "#0d0d0d", + "overlay": "#fdfdfd", + "text": "#ffffff", + "subtext": "#ececec", + "muted": "#fdfdfd", + "accent": "#8d8d8d", + "accentAlt": "#9b9b9b", + "good": "#b6b6b6", + "warn": "#cecece", + "bad": "#a4a4a4" + }, + "ansi": [ + "#0d0d0d", + "#a4a4a4", + "#b6b6b6", + "#cecece", + "#8d8d8d", + "#9b9b9b", + "#b0b0b0", + "#ececec", + "#fdfdfd", + "#a4a4a4", + "#b6b6b6", + "#cecece", + "#8d8d8d", + "#9b9b9b", + "#b0b0b0", + "#ffffff" + ] +} diff --git a/themes/palettes/vantablack/backgrounds/1-twisted-stairs.jpg b/themes/vantablack/backgrounds/1-twisted-stairs.jpg similarity index 100% rename from themes/palettes/vantablack/backgrounds/1-twisted-stairs.jpg rename to themes/vantablack/backgrounds/1-twisted-stairs.jpg diff --git a/themes/palettes/vantablack/backgrounds/2-layers-deep.jpg b/themes/vantablack/backgrounds/2-layers-deep.jpg similarity index 100% rename from themes/palettes/vantablack/backgrounds/2-layers-deep.jpg rename to themes/vantablack/backgrounds/2-layers-deep.jpg diff --git a/themes/palettes/vantablack/backgrounds/3-layers-stacked.jpg b/themes/vantablack/backgrounds/3-layers-stacked.jpg similarity index 100% rename from themes/palettes/vantablack/backgrounds/3-layers-stacked.jpg rename to themes/vantablack/backgrounds/3-layers-stacked.jpg diff --git a/themes/palettes/vantablack/apps/btop.theme b/themes/vantablack/btop.theme similarity index 100% rename from themes/palettes/vantablack/apps/btop.theme rename to themes/vantablack/btop.theme diff --git a/themes/white.json b/themes/white.json new file mode 100644 index 0000000..b65f62f --- /dev/null +++ b/themes/white.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "name": "White", + "slug": "white", + "mode": "light", + "wallpaper": "", + "colors": { + "base": "#ffffff", + "mantle": "#d9d9d9", + "surface": "#ffffff", + "overlay": "#c0c0c0", + "text": "#000000", + "subtext": "#000000", + "muted": "#c0c0c0", + "accent": "#6e6e6e", + "accentAlt": "#2e2e2e", + "good": "#3a3a3a", + "warn": "#4a4a4a", + "bad": "#2a2a2a" + }, + "ansi": [ + "#ffffff", + "#2a2a2a", + "#3a3a3a", + "#4a4a4a", + "#1a1a1a", + "#2e2e2e", + "#3e3e3e", + "#000000", + "#c0c0c0", + "#2a2a2a", + "#3a3a3a", + "#4a4a4a", + "#1a1a1a", + "#2e2e2e", + "#3e3e3e", + "#000000" + ] +} diff --git a/themes/palettes/white/backgrounds/1-white.jpg b/themes/white/backgrounds/1-white.jpg similarity index 100% rename from themes/palettes/white/backgrounds/1-white.jpg rename to themes/white/backgrounds/1-white.jpg diff --git a/themes/palettes/white/backgrounds/2-white.jpg b/themes/white/backgrounds/2-white.jpg similarity index 100% rename from themes/palettes/white/backgrounds/2-white.jpg rename to themes/white/backgrounds/2-white.jpg diff --git a/themes/palettes/white/backgrounds/3-white.jpg b/themes/white/backgrounds/3-white.jpg similarity index 100% rename from themes/palettes/white/backgrounds/3-white.jpg rename to themes/white/backgrounds/3-white.jpg diff --git a/themes/palettes/white/apps/btop.theme b/themes/white/btop.theme similarity index 100% rename from themes/palettes/white/apps/btop.theme rename to themes/white/btop.theme diff --git a/tools/import-palettes.py b/tools/import-palettes.py new file mode 100644 index 0000000..63c32a1 --- /dev/null +++ b/tools/import-palettes.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Convert old-distro palettes (colors.toml) into Nomarchy theme JSON presets. + +Usage: tools/import-palettes.py <old-palettes-dir> <output-themes-dir> + +Old format (per theme directory): + colors.toml kitty-style keys: background, foreground, accent, + selection_background, color0..color15 + light.mode marker file: theme is light + icons.theme icon theme name (informational, ignored for now) + backgrounds/ wallpapers + apps/ hand-made per-app theme files (btop.theme, ...) + +Output, per theme: + <slug>.json the palette (Nomarchy theme schema) + <slug>/backgrounds/ wallpapers, copied verbatim + <slug>/btop.theme copied from apps/ when present +""" + +import json +import shutil +import sys +import tomllib +from pathlib import Path + + +def darken(hex_color: str, factor: float = 0.85) -> str: + """Scale a #rrggbb color toward black (used to derive 'mantle').""" + h = hex_color.lstrip("#") + r, g, b = (int(h[i:i + 2], 16) for i in (0, 2, 4)) + return "#{:02x}{:02x}{:02x}".format(*(round(v * factor) for v in (r, g, b))) + + +def convert(theme_dir: Path) -> dict: + toml = tomllib.loads((theme_dir / "colors.toml").read_text()) + slug = theme_dir.name + ansi = [toml[f"color{i}"] for i in range(16)] + + return { + "version": 1, + "name": slug.replace("-", " ").title(), + "slug": slug, + "mode": "light" if (theme_dir / "light.mode").exists() else "dark", + # Empty = auto: nomarchy-theme-sync falls back to the first file in + # the theme's backgrounds/ directory. + "wallpaper": "", + "colors": { + "base": toml["background"], + "mantle": darken(toml["background"]), + "surface": toml["color0"], + "overlay": toml["color8"], + "text": toml["foreground"], + "subtext": toml["color7"], + "muted": toml["color8"], + "accent": toml["accent"], + "accentAlt": toml["color5"], + "good": toml["color2"], + "warn": toml["color3"], + "bad": toml["color1"], + }, + "ansi": ansi, + } + + +def main() -> None: + if len(sys.argv) != 3: + sys.exit(__doc__) + src, out = Path(sys.argv[1]), Path(sys.argv[2]) + out.mkdir(parents=True, exist_ok=True) + + converted = 0 + for theme_dir in sorted(src.iterdir()): + if not (theme_dir / "colors.toml").is_file(): + continue + theme = convert(theme_dir) + slug = theme["slug"] + (out / f"{slug}.json").write_text(json.dumps(theme, indent=2) + "\n") + + # Assets: wallpapers and hand-made per-app theme files. + extras = [] + if (theme_dir / "backgrounds").is_dir(): + shutil.copytree(theme_dir / "backgrounds", out / slug / "backgrounds", + dirs_exist_ok=True) + extras.append(f"{len(list((theme_dir / 'backgrounds').iterdir()))} wallpapers") + if (theme_dir / "apps" / "btop.theme").is_file(): + (out / slug).mkdir(parents=True, exist_ok=True) + shutil.copyfile(theme_dir / "apps" / "btop.theme", out / slug / "btop.theme") + extras.append("btop") + + print(f" {slug:<20} ({theme['mode']}{', ' + ', '.join(extras) if extras else ''})") + converted += 1 + print(f"converted {converted} palettes -> {out}") + + +if __name__ == "__main__": + main() diff --git a/tools/test-install.sh b/tools/test-install.sh new file mode 100755 index 0000000..48cd1a2 --- /dev/null +++ b/tools/test-install.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash +# End-to-end regression test for nomarchy-install: build the live ISO, +# run a fully OFFLINE unattended install into a fresh VM disk, then boot +# the installed system and capture what the first boot looks like. +# +# This automates the verification protocol from docs/TESTING.md §4 that +# debugged the installer in the first place. It asserts the machine- +# checkable parts (installer reaches poweroff; installed disk boots); +# the visual part — a fully themed desktop, no autogenerated-config +# banner — lands as screenshots for a human (or agent) to inspect: +# +# $NOMARCHY_VM_DIR/install-typed.png the typed command, pre-enter +# $NOMARCHY_VM_DIR/luks-prompt.png initrd passphrase prompt +# $NOMARCHY_VM_DIR/first-boot.png THE result: themed desktop +# +# Requirements: KVM, qemu, OVMF, dosfstools (mkfs.vfat), mtools (mcopy), +# python3. Tunables (env): +# NOMARCHY_VM_DIR scratch + screenshots (default /tmp/nomarchy-vm) +# NOMARCHY_TARGET_DIR where the 25G target.img lives — must NOT be +# tmpfs (default ~/.cache/nomarchy-vm) +# NOMARCHY_BOOT_WAIT seconds to wait for the live desktop (default 480) +# NOMARCHY_INSTALL_TIMEOUT seconds before declaring a hang (default 2700) + +set -euo pipefail +cd "$(dirname "$0")/.." + +VM_DIR="${NOMARCHY_VM_DIR:-/tmp/nomarchy-vm}" +TARGET_DIR="${NOMARCHY_TARGET_DIR:-$HOME/.cache/nomarchy-vm}" +BOOT_WAIT="${NOMARCHY_BOOT_WAIT:-480}" +INSTALL_TIMEOUT="${NOMARCHY_INSTALL_TIMEOUT:-2700}" +export NOMARCHY_QMP_SOCK="$VM_DIR/qmp.sock" +QMP=tools/vm/qmp.py +SHOT=tools/vm/vncshot.py +VNC_DISPLAY=17 # port 5917 + +for tool in qemu-system-x86_64 mkfs.vfat mcopy python3; do + command -v "$tool" >/dev/null || { + echo "missing $tool — try: nix shell nixpkgs#qemu nixpkgs#dosfstools nixpkgs#mtools" >&2 + exit 1 + } +done +[ -r /dev/kvm ] || { echo "no KVM — this test is too slow without it" >&2; exit 1; } + +mkdir -p "$VM_DIR" "$TARGET_DIR" +if df --output=fstype "$TARGET_DIR" | tail -1 | grep -q tmpfs; then + echo "NOMARCHY_TARGET_DIR=$TARGET_DIR is tmpfs — the install writes ~10G; pick a real disk" >&2 + exit 1 +fi + +OVMF="" +for c in /run/current-system/sw/share/qemu/edk2-x86_64-code.fd \ + /run/current-system/sw/share/OVMF/OVMF_CODE.fd \ + /usr/share/OVMF/OVMF_CODE.fd; do + [ -f "$c" ] && { OVMF="$c"; break; } +done +[ -n "$OVMF" ] || { echo "no OVMF firmware found" >&2; exit 1; } + +echo "==> Building the live ISO..." +nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage +ISO=$(ls -1 result/iso/*.iso | head -n1) + +echo "==> Preparing disks..." +# Unattended config rides in on a tiny vfat disk: the installer test +# can't reliably TYPE a 200-char env line into the guest (keystrokes +# get dropped), but one short mount-and-run command works. +cat > "$VM_DIR/go.sh" <<'EOF' +#!/usr/bin/env bash +export NOMARCHY_UNATTENDED=1 +export NOMARCHY_DISK=/dev/vda +export NOMARCHY_USERNAME=ada +export NOMARCHY_PASSWORD=nomarchy +export NOMARCHY_HOSTNAME=testbox +export NOMARCHY_LUKS_PASSPHRASE=testtest1 +export NOMARCHY_SWAP_GB=2 +export NOMARCHY_FINISH=poweroff +nomarchy-install 2>&1 | tee /tmp/install.log +EOF +rm -f "$VM_DIR/config.img" +truncate -s 8M "$VM_DIR/config.img" +mkfs.vfat "$VM_DIR/config.img" >/dev/null +mcopy -i "$VM_DIR/config.img" "$VM_DIR/go.sh" ::go.sh + +rm -f "$TARGET_DIR/target.img" "$NOMARCHY_QMP_SOCK" +qemu-img create -f raw "$TARGET_DIR/target.img" 25G >/dev/null + +qemu_base=( + qemu-system-x86_64 + -enable-kvm -cpu host -m 6144 -smp 2 + -drive "if=pflash,format=raw,readonly=on,file=$OVMF" + -device virtio-vga-gl -display egl-headless + -vnc "127.0.0.1:$VNC_DISPLAY" + -device virtio-net-pci,netdev=n0 -netdev user,id=n0,restrict=on # OFFLINE + -qmp "unix:$NOMARCHY_QMP_SOCK,server,nowait" +) + +echo "==> Phase 1: installing (offline) — this takes ~20-30 min..." +"${qemu_base[@]}" \ + -drive "file=$TARGET_DIR/target.img,if=virtio,format=raw" \ + -drive "file=$VM_DIR/config.img,if=virtio,format=raw,readonly=on" \ + -cdrom "$ISO" -boot d \ + >"$VM_DIR/qemu-install.log" 2>&1 & +QPID=$! +trap 'kill $QPID 2>/dev/null || true' EXIT + +until [ -S "$NOMARCHY_QMP_SOCK" ]; do sleep 2; done +echo " waiting ${BOOT_WAIT}s for the live desktop..." +sleep "$BOOT_WAIT" +python3 "$QMP" key meta_l-ret +sleep 8 +python3 "$QMP" type 'sudo bash -c "mkdir -p /m; mount /dev/vdb /m; bash /m/go.sh"' +sleep 2 +python3 "$SHOT" "$VM_DIR/install-typed.png" 127.0.0.1 "59$VNC_DISPLAY" +python3 "$QMP" key ret +echo " install running (typed command captured in install-typed.png)..." + +waited=0 +while kill -0 $QPID 2>/dev/null; do + if (( waited >= INSTALL_TIMEOUT )); then + python3 "$SHOT" "$VM_DIR/install-hung.png" 127.0.0.1 "59$VNC_DISPLAY" || true + echo "FAIL: installer did not power off within ${INSTALL_TIMEOUT}s (see install-hung.png)" >&2 + exit 1 + fi + sleep 20; waited=$((waited + 20)) +done +trap - EXIT +echo " VM powered off after ~${waited}s — install completed." + +echo "==> Phase 2: first boot of the installed system..." +rm -f "$NOMARCHY_QMP_SOCK" +"${qemu_base[@]}" \ + -drive "file=$TARGET_DIR/target.img,if=virtio,format=raw" \ + >"$VM_DIR/qemu-boot.log" 2>&1 & +QPID=$! +trap 'kill $QPID 2>/dev/null || true' EXIT + +until [ -S "$NOMARCHY_QMP_SOCK" ]; do sleep 2; done +sleep 40 +python3 "$SHOT" "$VM_DIR/luks-prompt.png" 127.0.0.1 "59$VNC_DISPLAY" +python3 "$QMP" type 'testtest1' +python3 "$QMP" key ret +sleep 150 +python3 "$SHOT" "$VM_DIR/first-boot.png" 127.0.0.1 "59$VNC_DISPLAY" +python3 "$QMP" quit || true +trap - EXIT + +echo +echo "PASS (machine-checkable part): install powered off, installed disk boots." +echo "Now INSPECT $VM_DIR/first-boot.png:" +echo " expected: themed desktop (wallpaper + waybar), NO red banner, no login prompt" +echo " if it shows the Hyprland water-drop or an 'autogenerated config' banner," +echo " log in on tty2 (ada/nomarchy) and read /var/log/nomarchy-hm-preactivate.log" diff --git a/features/scripts/utils/nomarchy-test-live-iso b/tools/test-live-iso.sh similarity index 61% rename from features/scripts/utils/nomarchy-test-live-iso rename to tools/test-live-iso.sh index 772c53e..92d52bf 100755 --- a/features/scripts/utils/nomarchy-test-live-iso +++ b/tools/test-live-iso.sh @@ -1,23 +1,22 @@ #!/usr/bin/env bash - -# Build the Nomarchy graphical live ISO and boot it in QEMU for a -# try-before-install experience. The ISO is the `nomarchy-live` -# NixOS configuration - same Nomarchy system + Home Manager stack as -# `nomarchy-test-vm`, just wrapped in an installable live medium. +# Build the Nomarchy live ISO and boot it in QEMU for a try-before-install +# run. Ported from the previous Nomarchy iteration. See docs/TESTING.md +# for what to verify once it's up. set -e +cd "$(dirname "$0")/.." -echo "Building Nomarchy Live ISO..." +echo "Building Nomarchy live ISO..." nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage ISO=$(ls -1 result/iso/*.iso 2>/dev/null | head -n 1) if [ -z "$ISO" ]; then - echo "Error: ISO build succeeded but no .iso file found in result/iso/" + echo "Error: ISO build succeeded but no .iso file found in result/iso/" >&2 exit 1 fi -# Prefer UEFI with OVMF so the ISO boots the same way a modern install does. -# Fall back to BIOS if OVMF isn't available on this host. +# Prefer UEFI with OVMF so the ISO boots the same way a modern install +# does; fall back to legacy BIOS if OVMF isn't available on this host. OVMF_CANDIDATES=( "/run/current-system/sw/share/OVMF/OVMF_CODE.fd" "/run/current-system/sw/share/qemu/edk2-x86_64-code.fd" @@ -27,25 +26,20 @@ OVMF_CANDIDATES=( BIOS_ARG=() for c in "${OVMF_CANDIDATES[@]}"; do if [ -f "$c" ]; then - # Use pflash for UEFI firmware. -bios is for legacy BIOS. BIOS_ARG=(-drive "if=pflash,format=raw,readonly=on,file=$c") - - # Optional: Add matching VARS file if it exists. VARS="${c%_CODE.fd}_VARS.fd" - if [ -f "$VARS" ]; then - BIOS_ARG+=(-drive "if=pflash,format=raw,readonly=on,file=$VARS") - fi + [ -f "$VARS" ] && BIOS_ARG+=(-drive "if=pflash,format=raw,readonly=on,file=$VARS") break fi done -# KVM if the host supports it; software emulation otherwise. +# KVM if the host supports it; software emulation otherwise (slow). ACCEL_ARG=() -if [ -r /dev/kvm ]; then - ACCEL_ARG=(-enable-kvm -cpu host) -fi +[ -r /dev/kvm ] && ACCEL_ARG=(-enable-kvm -cpu host) echo "Launching ISO: $ISO" +# virtio-vga-gl + gl=on gives the guest real OpenGL — Hyprland and +# Ghostty need it; without GL the session may fail to start. exec qemu-system-x86_64 \ "${ACCEL_ARG[@]}" \ -m 4096 -smp 2 \ diff --git a/tools/vm/gap-analysis.py b/tools/vm/gap-analysis.py new file mode 100755 index 0000000..4688db3 --- /dev/null +++ b/tools/vm/gap-analysis.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Which store paths would an offline nixos-install need that the ISO lacks? + +Walk the target drv graph: an output that's PRESENT needs nothing; a +missing output forces building its drv, which needs all its input +sources present and all referenced input outputs (recursively). + +This is the maintainer tool that converged the offline pin set in +flake.nix (extraDependencies): when an offline install builds from +source, run this instead of bisecting the VM. Healthy output is a few +dozen per-machine config drvs (etc, initrd-*, unit-*, …) and zero +[FETCH/NETWORK] leaves; any real package name here needs pinning. + +Usage: + # 1. the present set = everything the ISO carries: + live=$(nix build --no-link --print-out-paths \ + .#nixosConfigurations.nomarchy-live.config.system.build.toplevel) + nix path-info -r "$live" | sort -u > /tmp/present.txt + # 2. instantiate an install-shaped toplevel for a FOREIGN + # username/hostname/uuids (see docs/TESTING.md) and: + gap-analysis.py <target.drv> /tmp/present.txt +""" +import json +import subprocess +import sys + +target_drv, present_file = sys.argv[1], sys.argv[2] +present = set(open(present_file).read().split()) + +graph = json.loads(subprocess.check_output( + ["nix", "derivation", "show", "-r", target_drv + "^*"])) + +must_build = set() +missing_srcs = set() +visited = set() + + +def outputs_of(drv_name): + return [o["path"] for o in graph[drv_name]["outputs"].values()] + + +def need_drv(drv_name): + if drv_name in visited: + return + visited.add(drv_name) + must_build.add(drv_name) + node = graph[drv_name] + for src in node.get("inputSrcs", []): + if src not in present: + missing_srcs.add(src) + for dep_drv, want in node.get("inputDrvs", {}).items(): + dep_outputs = graph.get(dep_drv) + if dep_outputs is None: + continue + wanted = want.get("outputs", []) if isinstance(want, dict) else want + for out_name in wanted: + path = graph[dep_drv]["outputs"][out_name]["path"] + if path not in present: + need_drv(dep_drv) + break + + +# Roots: the target's own outputs are by definition "to build". +need_drv(target_drv) + +print(f"drvs that must be built offline: {len(must_build)}") +for d in sorted(must_build): + name = d.split("-", 1)[1] if "-" in d else d + builder = graph[d].get("builder", "") + fetch = " [FETCH/NETWORK]" if graph[d].get("env", {}).get("urls") or graph[d].get("env", {}).get("url") else "" + print(f" {name}{fetch}") +print(f"missing input sources: {len(missing_srcs)}") +for s in sorted(missing_srcs): + print(f" SRC {s}") diff --git a/tools/vm/qmp.py b/tools/vm/qmp.py new file mode 100755 index 0000000..644ce7a --- /dev/null +++ b/tools/vm/qmp.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""Minimal QMP driver for the Nomarchy test VMs. + +Drives a QEMU instance through its QMP socket: inject keystrokes, type +text, take screendumps, power off. Used by tools/test-install.sh; also +handy interactively while debugging a VM that has no SSH. + +Usage: + qmp.py key <combo> # e.g. meta_l-ret, ctrl-alt-f2, ret + qmp.py type <text...> # types literal text (US layout) + qmp.py screenshot <out.png> # NOTE: with virtio-vga-gl this fails + # ("no surface") once the guest uses GL — + # use vncshot.py instead for desktops + qmp.py quit # hard power-off + +The socket path comes from $NOMARCHY_QMP_SOCK +(default /tmp/nomarchy-vm/qmp.sock). + +Keystroke pacing: the guest drops keys when busy (especially during +boot). $NOMARCHY_TYPE_DELAY (seconds between keys, default 0.08) trades +speed for reliability; verify long typed lines with a screenshot before +pressing enter. +""" +import json +import os +import socket +import sys +import time + +SOCK = os.environ.get("NOMARCHY_QMP_SOCK", "/tmp/nomarchy-vm/qmp.sock") +TYPE_DELAY = float(os.environ.get("NOMARCHY_TYPE_DELAY", "0.08")) + +CHAR_KEYS = { + " ": "spc", "-": "minus", "=": "equal", "[": "bracket_left", + "]": "bracket_right", ";": "semicolon", "'": "apostrophe", + "`": "grave_accent", "\\": "backslash", ",": "comma", ".": "dot", + "/": "slash", "\n": "ret", "\t": "tab", +} +SHIFT_CHARS = { + "!": "1", "@": "2", "#": "3", "$": "4", "%": "5", "^": "6", + "&": "7", "*": "8", "(": "9", ")": "0", "_": "minus", "+": "equal", + "{": "bracket_left", "}": "bracket_right", ":": "semicolon", + '"': "apostrophe", "~": "grave_accent", "|": "backslash", + "<": "comma", ">": "dot", "?": "slash", +} + + +class QMP: + def __init__(self): + self.s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.s.connect(SOCK) + self.f = self.s.makefile("rw") + self._read() # greeting + self.cmd("qmp_capabilities") + + def _read(self): + while True: + msg = json.loads(self.f.readline()) + if "event" in msg: + continue + return msg + + def cmd(self, name, **args): + self.f.write(json.dumps({"execute": name, "arguments": args}) + "\n") + self.f.flush() + resp = self._read() + if "error" in resp: + raise RuntimeError(f"{name}: {resp['error']}") + return resp + + def send_keys(self, names, hold=40): + keys = [{"type": "qcode", "data": n} for n in names] + self.cmd("send-key", keys=keys, **{"hold-time": hold}) + + def type_text(self, text): + for ch in text: + if ch.isalpha() and ch.lower() != ch: + self.send_keys(["shift", ch.lower()]) + elif ch in SHIFT_CHARS: + self.send_keys(["shift", SHIFT_CHARS[ch]]) + elif ch in CHAR_KEYS: + self.send_keys([CHAR_KEYS[ch]]) + else: + self.send_keys([ch]) + time.sleep(TYPE_DELAY) + + +def main(): + q = QMP() + cmd = sys.argv[1] + if cmd == "screenshot": + q.cmd("screendump", filename=sys.argv[2], format="png") + elif cmd == "key": + q.send_keys(sys.argv[2].split("-")) + elif cmd == "type": + q.type_text(" ".join(sys.argv[2:])) + elif cmd == "quit": + q.f.write(json.dumps({"execute": "quit"}) + "\n") + q.f.flush() + else: + sys.exit(f"unknown command {cmd}") + + +if __name__ == "__main__": + main() diff --git a/tools/vm/vncshot.py b/tools/vm/vncshot.py new file mode 100755 index 0000000..d9efd9d --- /dev/null +++ b/tools/vm/vncshot.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Grab one full framebuffer from a no-auth VNC server and save as PNG. + +Why VNC and not QMP screendump: with virtio-vga-gl the guest renders +into a GL scanout and screendump fails with "no surface"; QEMU's VNC +server reads the GL framebuffer back, so this always works. Pair the VM +with `-display egl-headless -vnc 127.0.0.1:17`. + +Stdlib only (manual RFB handshake + raw-encoding framebuffer + PNG +writer) so it runs anywhere python3 exists. + +Usage: vncshot.py <out.png> [host] [port] # default 127.0.0.1:5917 +""" +import socket, struct, sys, zlib + +def recv_exact(s, n): + buf = b"" + while len(buf) < n: + chunk = s.recv(n - len(buf)) + if not chunk: + raise EOFError("server closed") + buf += chunk + return buf + +def png_write(path, w, h, rgb): + def chunk(tag, data): + c = struct.pack(">I", len(data)) + tag + data + return c + struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF) + raw = b"".join(b"\x00" + rgb[y * w * 3:(y + 1) * w * 3] for y in range(h)) + out = (b"\x89PNG\r\n\x1a\n" + + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0)) + + chunk(b"IDAT", zlib.compress(raw, 6)) + + chunk(b"IEND", b"")) + with open(path, "wb") as f: + f.write(out) + +def main(): + out = sys.argv[1] + host = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1" + port = int(sys.argv[3]) if len(sys.argv) > 3 else 5917 + s = socket.create_connection((host, port), timeout=30) + s.settimeout(30) + + ver = recv_exact(s, 12) # "RFB 003.008\n" + s.sendall(b"RFB 003.008\n") + ntypes = recv_exact(s, 1)[0] + types = recv_exact(s, ntypes) + if 1 not in types: + sys.exit(f"no 'None' security type offered: {list(types)}") + s.sendall(b"\x01") + res = struct.unpack(">I", recv_exact(s, 4))[0] + if res != 0: + sys.exit("security handshake failed") + s.sendall(b"\x01") # ClientInit: shared + w, h = struct.unpack(">HH", recv_exact(s, 4)) + recv_exact(s, 16) # server pixel format (ignored) + namelen = struct.unpack(">I", recv_exact(s, 4))[0] + recv_exact(s, namelen) + + # SetPixelFormat: 32bpp truecolor, shifts r=16 g=8 b=0 (little-endian BGRX) + pf = struct.pack(">BBBBHHHBBBxxx", 32, 24, 0, 1, 255, 255, 255, 16, 8, 0) + s.sendall(b"\x00\x00\x00\x00" + pf) + s.sendall(struct.pack(">BxH i", 2, 1, 0)) # SetEncodings: Raw only + s.sendall(struct.pack(">BBHHHH", 3, 0, 0, 0, w, h)) # full update request + + fb = bytearray(w * h * 4) + got = False + while not got: + mtype = recv_exact(s, 1)[0] + if mtype == 0: # FramebufferUpdate + recv_exact(s, 1) + nrects = struct.unpack(">H", recv_exact(s, 2))[0] + for _ in range(nrects): + x, y, rw, rh = struct.unpack(">HHHH", recv_exact(s, 8)) + enc = struct.unpack(">i", recv_exact(s, 4))[0] + if enc == 0: + data = recv_exact(s, rw * rh * 4) + for row in range(rh): + off = ((y + row) * w + x) * 4 + fb[off:off + rw * 4] = data[row * rw * 4:(row + 1) * rw * 4] + elif enc == -224: # LastRect + break + else: + sys.exit(f"unsupported encoding {enc}") + got = True + elif mtype == 2: # Bell + pass + elif mtype == 3: # ServerCutText + recv_exact(s, 3) + ln = struct.unpack(">I", recv_exact(s, 4))[0] + recv_exact(s, ln) + else: + sys.exit(f"unexpected server message {mtype}") + + rgb = bytearray(w * h * 3) + for i in range(w * h): + rgb[i * 3 + 0] = fb[i * 4 + 2] + rgb[i * 3 + 1] = fb[i * 4 + 1] + rgb[i * 3 + 2] = fb[i * 4 + 0] + png_write(out, w, h, bytes(rgb)) + print(f"saved {out} ({w}x{h})") + +if __name__ == "__main__": + main()