Compare commits
13 Commits
v1
...
e1cf190dd2
| Author | SHA1 | Date | |
|---|---|---|---|
| e1cf190dd2 | |||
|
|
70334e68bb | ||
|
|
6a4af69b0f | ||
|
|
d9466d6555 | ||
|
|
0e42763aea | ||
|
|
a6d6860054 | ||
|
|
5c43a93285 | ||
|
|
09c308b93c | ||
|
|
bdf20f2d8e | ||
|
|
c57d26864e | ||
|
|
019fdfc8bb | ||
|
|
c2281dbc61 | ||
|
|
b4fe52261b |
11
README.md
@@ -190,7 +190,8 @@ examples: **[docs/OVERRIDES.md](docs/OVERRIDES.md)**.
|
||||
| `nomarchy.keyboard.variant` | `""` | XKB variant for the session |
|
||||
| `nomarchy.keyboard.devices` | `{}` | Per-device layout overrides (Hyprland `device` blocks keyed by `hyprctl devices` name) — e.g. an external keyboard with its own layout/variant |
|
||||
| `nomarchy.keyboard.layouts` | `[]` | Extra candidate layouts; when set, a watcher prompts (rofi) for a layout on a newly-connected keyboard and remembers it per-device |
|
||||
| `nomarchy.nightlight.enable` | `false` | Scheduled blue-light filter (hyprsunset) — warm at night (`.temperature`, default 4000K) between `.sunset`/`.sunrise`, no shift by day |
|
||||
| `nomarchy.nightlight.enable` | `false` | Scheduled blue-light filter (hyprsunset) — warm at night (`.temperature`, default 4000K) between `.sunset`/`.sunrise`, no shift by day; toggle it on/off from the menu (System › Night light) or the Waybar moon/sun indicator |
|
||||
| `nomarchy.updates.enable` | `false` | Passive update awareness: a background check (`.interval`, default daily) that flags when flake inputs (nixpkgs, the Nomarchy input, …) are behind upstream — and, with Flatpak on, when apps have updates (`.flatpak`) — via a Waybar indicator + notification. Never changes anything; click the indicator to run the upgrade flow |
|
||||
| `nomarchy.hyprland.enable` | `true` | Nomarchy's Hyprland config |
|
||||
| `nomarchy.waybar.enable` | `true` | Nomarchy's Waybar |
|
||||
| `nomarchy.rofi.enable` | `true` | Themed rofi launcher + `nomarchy-menu` dispatcher |
|
||||
@@ -219,6 +220,14 @@ examples: **[docs/OVERRIDES.md](docs/OVERRIDES.md)**.
|
||||
| `nomarchy.system.power.laptop` | `false` | Marks a laptop, gating battery-only features; the installer sets it when a battery is present |
|
||||
| `nomarchy.system.power.thermal.enable` | `false` | thermald (Intel-only); the installer enables it on a GenuineIntel CPU |
|
||||
| `nomarchy.system.power.batteryChargeLimit` | `null` | Stop charging at this % (e.g. `80`) where the hardware supports it; needs `power.laptop` |
|
||||
| `nomarchy.hardware.intel.enable` | `false` | Intel enablement above nixos-hardware (GuC/HuC via `i915.enable_guc=3`); the installer sets it on an Intel CPU/GPU |
|
||||
| `nomarchy.hardware.intel.computeRuntime` | `false` | Opt-in: Intel GPU compute — OpenCL/Level-Zero (`intel-compute-runtime`) + oneVPL (`vpl-gpu-rt`) |
|
||||
| `nomarchy.hardware.amd.enable` | `false` | AMD enablement above nixos-hardware (amd-pstate EPP + radeonsi VA-API); installer-set on an AMD CPU/GPU |
|
||||
| `nomarchy.hardware.amd.rocm.enable` | `false` | Opt-in: ROCm HIP/OpenCL GPU compute (multi-GB); pair with `.gfxOverride` (e.g. `"11.0.0"`) for an unlisted iGPU |
|
||||
| `nomarchy.hardware.fingerprint.enable` | `false` | fprintd for a detected fingerprint reader (installer-set); enroll with `fprintd-enroll` |
|
||||
| `nomarchy.hardware.fingerprint.pam` | `false` | Opt-in: use the fingerprint for login + sudo (PAM) |
|
||||
| `nomarchy.hardware.npu.enable` | `false` | Opt-in/experimental: load the on-die NPU driver (`amdxdna`/`intel_vpu`); userspace runtime is BYO |
|
||||
| `nomarchy.hardware.latestKernel` | `false` | Opt-in: ship `linuxPackages_latest` instead of the default kernel — for very new hardware whose drivers landed recently |
|
||||
| `nomarchy.services.tailscale.enable` | `false` | Opt-in: Tailscale mesh VPN (then `sudo tailscale up`) |
|
||||
| `nomarchy.services.syncthing.enable` | `false` | Opt-in: Syncthing file sync as the login user (GUI at `127.0.0.1:8384`) |
|
||||
| `nomarchy.services.podman.enable` | `false` | Opt-in: rootless Podman (`docker` aliased to it) |
|
||||
|
||||
163
docs/ROADMAP.md
@@ -51,6 +51,11 @@ how to override it. Items marked ✓ are shipped.
|
||||
one-off — any new feature that earns a menu entry must be placed in the
|
||||
right submenu (don't let the root creep back to a flat list), with its
|
||||
direct `SUPER+CTRL+<mnemonic>` bind and self-gating as applicable.
|
||||
- ✓ **Back everywhere:** every list menu now ends with a `↩ Back` entry that
|
||||
returns one level up (power/theme/power-profile/clipboard/files/capture,
|
||||
not just Tools/System), so you never have to Esc out and reopen. A `back`
|
||||
helper + a shared `BACK` label keep it uniform; matched exactly so it can't
|
||||
collide with clipboard/filename content. Esc still quits instantly.
|
||||
- ✓ **Menu modules from rofi plugins:** the old `calc` flow committed the
|
||||
expression blind (result only in the *next* menu's `-mesg`) and `qalc -t`
|
||||
misparsed common phrasings (`15% of 200` → `rem(15, 1 B)`, the natural-
|
||||
@@ -83,9 +88,56 @@ how to override it. Items marked ✓ are shipped.
|
||||
green inputbar, yellow bottom-border). Remaining: author `.rasi`
|
||||
identities for the other four ported themes if/when they want one (the
|
||||
generated palette theme is the default and looks fine)
|
||||
- **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
|
||||
- ✓ **Visual theme picker (preview thumbnails):** `nomarchy-menu theme` is now
|
||||
a rofi **icon grid of real desktop previews** instead of a plain-text list —
|
||||
each theme a screenshot of its themed desktop (waybar + floating terminal),
|
||||
pretty name beneath, **grouped dark-first then light** in one scrollable grid
|
||||
(the previews make the mode obvious, so no light/dark submenu split), the
|
||||
active theme marked `✓`, ending in `↩ Back`. The grid, the Name→slug map and
|
||||
the active mark are all **generated at eval time** from the preset JSONs in
|
||||
`rofi.nix` (`builtins.readDir` + `fromJSON`); "active" is just `t.slug`, since
|
||||
every switch rebuilds the menu. Grid layout via a per-invocation `-theme-str`
|
||||
(`listview { columns: 3; flow: horizontal; }` so Down scrolls row-by-row, +
|
||||
vertical `element` cards, name centred below). **Sizing gotcha (learned the
|
||||
hard way):** rofi's `element-icon` `size` is a **single value → a square
|
||||
cell** (a two-value `WxH` is silently collapsed), and the icon is *contained*
|
||||
in that square. So a 16:9 preview letterboxes (theme-coloured bands top/
|
||||
bottom), and a cell can't be shorter-than-square without the bands returning.
|
||||
The fix: a build-time imagemagick step **centre-crops each preview to a
|
||||
square** so it fills the cell edge-to-edge; the window width is derived so a
|
||||
column is exactly the icon side (no slack margins). One knob, `themeGridIconW`
|
||||
(240px), drives both icon and window. **Workflow:** Bernardo captures previews
|
||||
on real hardware and commits them as `themes/<slug>/preview.png`, **already
|
||||
downscaled to 480×270** (~2.4 MB total for all 21, vs ~28 MB full-res) — the
|
||||
source stays 16:9 and untouched; only the *displayed* thumb is squared at
|
||||
build, so the crop is reversible. Graceful fallback when a theme has no
|
||||
`preview.png`: a plain-name row, so it degrades cleanly.
|
||||
(A headless VM-render route — `runNixOSTest` + software-GL Hyprland
|
||||
(`LIBGL_ALWAYS_SOFTWARE` on virtio-gpu) + `machine.screenshot()` QMP
|
||||
framebuffer dump — was prototyped 2026-06-19 and **works** (themed waybar
|
||||
rendered + captured); real-hardware capture was chosen for fidelity +
|
||||
simplicity, with the VM route as a documented fallback if hand-capturing all
|
||||
themes gets tedious.)
|
||||
- **Faster switches:** move `backgrounds/` out of the flake source. Diagnosis
|
||||
(confirmed): `themes/` is 86 MB and `backgrounds/` is **all** of it — the
|
||||
palette JSONs + per-theme overrides are only ~208 KB, and the wallpapers are
|
||||
**never read at Nix eval** (only the Python `swww` path uses them). But
|
||||
`theme-state.json` is git-tracked, so every `apply` rewrites it → the flake
|
||||
tree changes → Nix re-copies the whole 86 MB source before `home-manager
|
||||
switch` can evaluate. You pay an 86 MB copy to change a 1 KB file.
|
||||
**Decided approach (deferred — don't want the extra moving part yet):**
|
||||
Option 1, a **separate pinned wallpapers artifact** — a `Nomarchy-wallpapers`
|
||||
repo or release tarball, pulled once via a flake input / `fetchurl` (pinned
|
||||
by hash → content-addressed, never re-copied on a state write), with
|
||||
`nomarchy-theme-sync` reading wallpapers from that stable store path (the
|
||||
`NOMARCHY_DEFAULT_THEMES` env hook already anticipates external theme
|
||||
assets). Keeps eval **pure**; the live ISO still bakes them in (fetched at
|
||||
build). A state write then re-copies only ~208 KB. Rejected alternative:
|
||||
moving `theme-state.json` out to `~/.config` + `--impure` eval (one repo, no
|
||||
second artifact, but trades away the in-tree-pinned-state reproducibility).
|
||||
Follow-on if `home-manager switch` itself is still the bottleneck after the
|
||||
copy is gone: **pre-built theme variants** (build each theme's generation
|
||||
ahead of time so a switch just activates a cached one).
|
||||
- Greeter (tuigreet/SDDM) theming from the same JSON (Plymouth ships since
|
||||
v1: `nomarchy.system.plymouth.*`, background tinted from the state file)
|
||||
- Installer round 2: multi-disk BTRFS RAID, impermanence, BIOS/legacy
|
||||
@@ -263,6 +315,31 @@ how to override it. Items marked ✓ are shipped.
|
||||
Complements nixos-hardware (model quirks) rather than replacing it; keep
|
||||
the detection in the installer's `hardware-db` so an installed machine
|
||||
bakes the right defaults, all overridable through `nomarchy.hardware.*`.
|
||||
- ✓ **Mechanism + broad seed shipped** (`modules/nixos/hardware.nix`): a
|
||||
vendor-keyed `nomarchy.hardware.*` surface (`intel`, `amd`, `fingerprint`,
|
||||
`npu`), `hardware-db.sh` extended to detect Intel/AMD, a fingerprint reader
|
||||
(libfprint USB vendor IDs) and an NPU (Intel VPU / AMD XDNA PCI IDs), and
|
||||
the installer bakes the matching toggles into `system.nix` — safe defaults
|
||||
active (GuC/HuC, amd-pstate, AMD VA-API env, fprintd), heavy/experimental
|
||||
opt-ins commented (Intel compute-runtime, ROCm, fingerprint PAM, NPU
|
||||
driver). Audited against the nixos-hardware commons so it only fills the
|
||||
gap above them — microcode / the media-driver VA-API stack / weekly fstrim
|
||||
already come from `common-cpu-*` / `common-gpu-*` / `common-pc-ssd`.
|
||||
Exercised live on the dev machine's detection (an AMD Ryzen-AI laptop: AMD
|
||||
+ fingerprint + NPU all detected) and a toggles-on build (`amd_pstate=active`
|
||||
+ `i915.enable_guc=3` in kernel-params, `fprintd.service` present).
|
||||
**Remaining: on-hardware verification of the runtime bits (AMD/NPU/Intel
|
||||
GPU-compute) — none are testable in CI or on the Intel Latitudes for the
|
||||
AMD paths.** SSD TRIM is intentionally left to `common-pc-ssd`.
|
||||
- ✓ **Hardening (driver-gen + kernel awareness):** `intel.guc` emits the
|
||||
*i915* param, so the installer turns it off when the GPU is on the newer
|
||||
`xe` driver (Lunar Lake / Battlemage / Panther Lake — GuC is default-on
|
||||
there); the NPU detector matches the PCI *accelerator class* (not just a
|
||||
device-ID list) so new gens are caught without a code change; and a
|
||||
`nomarchy.hardware.latestKernel` escape hatch (+ a build-time warning when
|
||||
`npu.enable` predates the shipped kernel) covers very-new hardware whose
|
||||
drivers only just landed. A config-assertion VM test guards the wiring
|
||||
(`checks.hardware-toggles`: kernel cmdline + fprintd + PAM, booted in a VM).
|
||||
Worked example — ThinkPad T14s (Ryzen 7 PRO 7840U + Radeon 780M):
|
||||
- **GPU compute — ROCm:** the 780M is an RDNA3 iGPU (gfx1103) ROCm doesn't
|
||||
officially list, so it needs `HSA_OVERRIDE_GFX_VERSION=11.0.0`. Multi-GB
|
||||
@@ -353,10 +430,24 @@ how to override it. Items marked ✓ are shipped.
|
||||
warm at night, identity (no shift) by day. `modules/home/nightlight.nix`
|
||||
drives the HM `services.hyprsunset` with two time-based profiles
|
||||
(`sunrise` → identity, `sunset` → `temperature`), so hyprsunset handles the
|
||||
schedule and the on-login state. Needs an on-hardware check that hyprsunset
|
||||
applies the active profile at session start. Remaining (optional): a menu +
|
||||
Waybar toggle to force it on/off, and geo (lat/long) auto sunset/sunrise
|
||||
(would mean wlsunset, which schedules by location).
|
||||
schedule and the on-login state. Active-profile-at-session-start was
|
||||
confirmed on hardware (2026-06-18). ✓ **Menu + Waybar toggle:**
|
||||
`nomarchy-nightlight` (in `nightlight.nix`, on PATH) starts/stops the
|
||||
hyprsunset *service* — `systemctl --user is-active` is the single source of
|
||||
truth, the screen restores to true colours when stopped, and it forces the
|
||||
filter off for colour-sensitive work and back on without touching config. A
|
||||
`nomarchy-menu nightlight` entry (System submenu, self-gated on the unit
|
||||
existing) and a self-gating Waybar `custom/nightlight` indicator (moon = on,
|
||||
sun = off; in the generated bar and both summer whole-swaps) drive it.
|
||||
The toggle is **session-scoped by design**: `stop` halts the running unit
|
||||
but it's `WantedBy=graphical-session.target`, so the next login auto-starts
|
||||
it and night-light returns to its schedule (off never persists). Remaining
|
||||
(optional): (1) **persistent off** — a state marker
|
||||
(`~/.local/state/nomarchy/nightlight-off`) the unit honours via
|
||||
`ConditionPathExists=!%h/…`, so a manual off survives logout/reboot until
|
||||
toggled back; (2) geo (lat/long) auto sunset/sunrise (would mean wlsunset,
|
||||
which schedules by location). Pending an on-machine check that stopping
|
||||
hyprsunset cleanly restores the gamma.
|
||||
- **Keyboard layouts (per-device + switching):**
|
||||
- ✓ **Per-device declarative layout:** `nomarchy.keyboard.devices`
|
||||
(`{ "<hyprctl-device-name>" = { layout; variant; }; }`) generates Hyprland
|
||||
@@ -372,15 +463,20 @@ how to override it. Items marked ✓ are shipped.
|
||||
watch` daemon runs (exec-once): it polls `hyprctl devices`, and when a
|
||||
keyboard connects *after* login that isn't in `keyboard.devices` and
|
||||
hasn't been chosen before, it pops a rofi layout picker, applies the
|
||||
choice with `hyprctl switchxkblayout` (an index into the candidate set,
|
||||
which `input.kb_layout` carries), and remembers it per-device in
|
||||
`~/.local/state/nomarchy/keyboard-layouts` — re-applied silently on later
|
||||
reconnects. The boot-time set (incl. the built-in keyboard) is never
|
||||
prompted. The runtime-remember complement to the declarative
|
||||
`keyboard.devices`; a stateful runtime piece by design. **Pending an
|
||||
on-hardware test** (hotplug isn't verifiable in CI). Remaining (bonus):
|
||||
offer to write a choice into `keyboard.devices` so it graduates to the
|
||||
reproducible config.
|
||||
choice as a **per-device** `hyprctl keyword device[<name>]:kb_layout` (the
|
||||
runtime twin of the declarative `device{}` blocks — so it isolates that
|
||||
one keyboard and never touches the built-in board), and remembers it
|
||||
per-device in `~/.local/state/nomarchy/keyboard-layouts` — re-applied
|
||||
silently on later reconnects. The boot-time set (incl. the built-in
|
||||
keyboard) is never prompted. The runtime-remember complement to the
|
||||
declarative `keyboard.devices`; a stateful runtime piece by design.
|
||||
**Picker verified on hardware (2026-06-18)**; the first cut applied the
|
||||
choice with `switchxkblayout` (a global layout-index flip) and merged the
|
||||
candidate pool into `input.kb_layout`, so a selection leaked onto the
|
||||
laptop keyboard and stuck after unplug — fixed by the per-device keyword +
|
||||
keeping the pool out of the session layout. Remaining (bonus): offer to
|
||||
write a choice into `keyboard.devices` so it graduates to the reproducible
|
||||
config; **re-verify the per-device isolation on hardware**.
|
||||
- Remaining: a multi-layout cycle bind (`hyprctl switchxkblayout` / xkb
|
||||
`grp:` options) for switching layouts on one keyboard; add the
|
||||
`hyprland/language` module to the summer whole-swap themes for parity
|
||||
@@ -410,15 +506,32 @@ how to override it. Items marked ✓ are shipped.
|
||||
artifact and will crash on hardware too). The menu wiring is correct and
|
||||
the app is installed, but the GUI doesn't open. The snapshot *backend*
|
||||
(the actual snapshots) is unaffected. Fix path: a nixpkgs override aligning
|
||||
its libbtrfsutil, or wait for an upstream bump; meanwhile the menu entry
|
||||
is a no-op when the binary crashes. Fallback if it lingers: a rofi-based
|
||||
snapshot menu (no btrfs-assistant dependency).
|
||||
- Remaining (optional): a keyboard-driven rofi browse for quick glances;
|
||||
boot-from-snapshot needs a systemd-boot equivalent of grub-btrfs.
|
||||
- **Update awareness:** updates are manual today (`sys-update`/`home-update`);
|
||||
add a Waybar indicator / notification when flake inputs are stale or a new
|
||||
nixpkgs rev is available (optionally with a pending-change count). Augments,
|
||||
never replaces, the explicit rebuild flow.
|
||||
its libbtrfsutil, or wait for an upstream bump. ✓ **Fallback shipped:**
|
||||
the menu now launches `nomarchy-snapshots` (system-side, gated on
|
||||
`nomarchy.system.snapper`) instead — a keyboard-driven snapper
|
||||
browser/restore with no btrfs-assistant dependency. snapper is root-only
|
||||
here (no `ALLOW_USERS`) and rofi can't run as root under Wayland, so it
|
||||
runs in a terminal via `sudo` (one password prompt) and uses `fzf` to pick:
|
||||
browse/diff (read-only), restore files (`undochange`), or roll back (root
|
||||
config only) — each behind a typed-`yes` confirmation, which is *safer*
|
||||
than the fat-fingerable one-click rofi rollback this entry originally
|
||||
avoided. btrfs-assistant stays installed for when nixpkgs fixes it.
|
||||
**Pending an on-machine check of the restore/rollback paths.**
|
||||
- Remaining (optional): boot-from-snapshot needs a systemd-boot equivalent
|
||||
of grub-btrfs.
|
||||
- ✓ **Update awareness:** `nomarchy.updates.enable` (opt-in, `modules/home/
|
||||
updates.nix`) — a `nomarchy-updates` checker on a systemd user timer
|
||||
(`.interval`, default daily) compares each **direct** branch-tracking flake
|
||||
input (nixpkgs, the Nomarchy input, home-manager, stylix … via `git
|
||||
ls-remote` vs the locked rev; offline → skipped, no false alarm) and, when
|
||||
the `flatpak` CLI is present (`.flatpak`), counts Flatpak updates. A
|
||||
self-gating Waybar `custom/updates` indicator (hidden until something's
|
||||
available; accent ` N`, signal-refreshed) + a notification that fires only
|
||||
when the count *grows* (so a daily timer never nags). Click → the upgrade
|
||||
flow in a terminal (`sys-update` / `flatpak update`, each confirmed). Purely
|
||||
passive — it never changes anything, augmenting the explicit rebuild flow.
|
||||
Indicator is in the generated bar and both summer whole-swaps. **Pending an
|
||||
on-machine check** (the ls-remote/notify/timer path needs a live session).
|
||||
- **"nomarchy" control center:** a single TUI/GUI front-end over the common
|
||||
toggles — theme, power profile, opt-in services, display, DND — built on
|
||||
the same `nomarchy-theme-sync` / `nomarchy.*` surface the menu already
|
||||
|
||||
25
flake.nix
@@ -134,6 +134,31 @@
|
||||
downstream.nixosConfigurations.default.config.system.build.toplevel;
|
||||
downstream-template-home =
|
||||
downstream.homeConfigurations.me.activationPackage;
|
||||
|
||||
# Config-level VM assertions for the nomarchy.hardware.* toggles —
|
||||
# what they SET (kernel cmdline, fprintd, PAM). Real hardware
|
||||
# behaviour (firmware/driver/device) needs bare metal and is out of
|
||||
# scope here. Imports only hardware.nix, so the VM stays minimal.
|
||||
hardware-toggles = pkgs.testers.runNixOSTest {
|
||||
name = "nomarchy-hardware-toggles";
|
||||
nodes.machine = { ... }: {
|
||||
imports = [ ./modules/nixos/hardware.nix ];
|
||||
nomarchy.hardware = {
|
||||
intel.enable = true;
|
||||
amd.enable = true;
|
||||
fingerprint = { enable = true; pam = true; };
|
||||
npu.enable = true;
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
cmdline = machine.succeed("cat /proc/cmdline")
|
||||
assert "amd_pstate=active" in cmdline, cmdline
|
||||
assert "i915.enable_guc=3" in cmdline, cmdline
|
||||
machine.succeed("systemctl cat fprintd.service")
|
||||
machine.succeed("grep -q pam_fprintd /etc/pam.d/sudo")
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# ─── Reference host ────────────────────────────────────────────
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
./yazi.nix # flagship TUI file manager, themed + plugins
|
||||
./osd.nix # swayosd volume/brightness OSD, themed
|
||||
./nightlight.nix # scheduled blue-light filter (hyprsunset), opt-in
|
||||
./updates.nix # passive update-awareness indicator + notification, opt-in
|
||||
./shell.nix # zsh + starship + bat/eza/zoxide, themed
|
||||
./keys.nix # gpg-agent (fronts SSH) + pinentry-qt
|
||||
./fastfetch.nix # system info with the themed Nomarchy logo
|
||||
|
||||
@@ -55,10 +55,14 @@ let
|
||||
++ lib.optional (m.extra != "") m.extra
|
||||
);
|
||||
|
||||
# Keyboard layout candidates: the primary nomarchy.keyboard.layout (split on
|
||||
# commas) plus any nomarchy.keyboard.layouts. Drives input.kb_layout so all
|
||||
# of them are switchable, and feeds the watcher's picker.
|
||||
kbLayouts = lib.unique (
|
||||
# Candidate layouts offered by the new-keyboard picker: the session
|
||||
# layout(s) (nomarchy.keyboard.layout, comma-split for a multi-layout
|
||||
# session) plus the extra nomarchy.keyboard.layouts pool. The pool is
|
||||
# deliberately NOT merged into input.kb_layout — those candidates are for
|
||||
# *external* keyboards and get applied per-device by the watcher's apply().
|
||||
# Loading them onto the session keyboard is what let a global switch flip
|
||||
# the built-in board to the wrong layout.
|
||||
pickerLayouts = lib.unique (
|
||||
(lib.splitString "," config.nomarchy.keyboard.layout)
|
||||
++ config.nomarchy.keyboard.layouts
|
||||
);
|
||||
@@ -68,18 +72,20 @@ let
|
||||
# keyboard.devices, not already remembered), ask for a layout and persist it
|
||||
# per-device, re-applying silently on later reconnects. Stateful runtime
|
||||
# piece — the complement to the declarative keyboard.devices. Reliable
|
||||
# primitives only: poll hyprctl devices, apply via switchxkblayout (an index
|
||||
# into kbLayouts). NOTE: needs an on-hardware test (hotplug isn't verifiable
|
||||
# in CI).
|
||||
# primitives only: poll hyprctl devices, apply with a per-device
|
||||
# `device[<name>]:kb_layout` keyword — the runtime twin of the declarative
|
||||
# device blocks, so it isolates that one keyboard and never disturbs the
|
||||
# built-in board (switchxkblayout flipped the *shared* layout, which leaked
|
||||
# onto the laptop and stuck after unplug). NOTE: needs an on-hardware test
|
||||
# (hotplug isn't verifiable in CI).
|
||||
keyboardWatch = pkgs.writeShellScriptBin "nomarchy-keyboard-watch" ''
|
||||
set -u
|
||||
layouts="${lib.concatStringsSep " " kbLayouts}"
|
||||
layouts="${lib.concatStringsSep " " pickerLayouts}"
|
||||
declared="${lib.concatStringsSep " " (builtins.attrNames config.nomarchy.keyboard.devices)}"
|
||||
state="''${XDG_STATE_HOME:-$HOME/.local/state}/nomarchy/keyboard-layouts"
|
||||
mkdir -p "$(dirname "$state")"; [ -f "$state" ] || : > "$state"
|
||||
|
||||
layout_index() { i=0; for l in $layouts; do [ "$l" = "$1" ] && { printf %s "$i"; return; }; i=$((i + 1)); done; printf %s -1; }
|
||||
apply() { idx=$(layout_index "$2"); [ "$idx" -ge 0 ] && hyprctl switchxkblayout "$1" "$idx" >/dev/null 2>&1; }
|
||||
apply() { hyprctl keyword "device[$1]:kb_layout" "$2" >/dev/null 2>&1; }
|
||||
keyboards() { hyprctl devices -j | jq -r '.keyboards[].name'; }
|
||||
is_declared() { case " $declared " in *" $1 "*) return 0 ;; *) return 1 ;; esac; }
|
||||
saved_for() { while IFS='=' read -r k v; do [ "$k" = "$1" ] && { printf '%s' "$v"; return; }; done < "$state"; }
|
||||
@@ -146,7 +152,13 @@ in
|
||||
# Paint the wallpaper as soon as the session is up (waits for
|
||||
# the daemon internally).
|
||||
"nomarchy-theme-sync wallpaper"
|
||||
] ++ lib.optional kbAutoSwitch "${keyboardWatch}/bin/nomarchy-keyboard-watch";
|
||||
# Waybar is launched here rather than as a systemd user service: bound
|
||||
# to graphical-session.target the unit raced Hyprland's IPC on relogin
|
||||
# and landed in `failed`; exec-once only fires once the compositor is
|
||||
# up. Reloaded on theme switch via SIGUSR2 (nomarchy-theme-sync). See
|
||||
# waybar.nix.
|
||||
] ++ lib.optional config.nomarchy.waybar.enable "waybar"
|
||||
++ lib.optional kbAutoSwitch "${keyboardWatch}/bin/nomarchy-keyboard-watch";
|
||||
|
||||
# ── Theme-driven look ──────────────────────────────────────────
|
||||
# These flow from theme-state.json and stay at NORMAL priority:
|
||||
@@ -201,8 +213,10 @@ in
|
||||
|
||||
input = {
|
||||
# kb_layout/variant come from nomarchy.keyboard.* — set that
|
||||
# option (the installer writes it; it also drives tty/LUKS).
|
||||
kb_layout = lib.concatStringsSep "," kbLayouts;
|
||||
# option (the installer writes it; it also drives tty/LUKS). Only the
|
||||
# session layout(s) land here; the keyboard.layouts pool is applied
|
||||
# per-device to external boards, never onto the session keyboard.
|
||||
kb_layout = config.nomarchy.keyboard.layout;
|
||||
kb_variant = config.nomarchy.keyboard.variant;
|
||||
follow_mouse = lib.mkDefault 1;
|
||||
touchpad.natural_scroll = lib.mkDefault true;
|
||||
@@ -270,7 +284,10 @@ in
|
||||
# writes a config file). A helper to find values for nomarchy.monitors —
|
||||
# the declarative config stays the source of truth (we don't source its
|
||||
# output). Gated on its toggle; pointless without the Hyprland session.
|
||||
home.packages = lib.optionals
|
||||
(config.nomarchy.hyprland.enable && config.nomarchy.displays.enable)
|
||||
[ pkgs.nwg-displays ];
|
||||
home.packages =
|
||||
lib.optionals (config.nomarchy.hyprland.enable && config.nomarchy.displays.enable)
|
||||
[ pkgs.nwg-displays ]
|
||||
# The new-keyboard watcher on PATH (when enabled) so it's discoverable and
|
||||
# runnable by name for debugging — Hyprland still exec-once's it by store path.
|
||||
++ lib.optional (config.nomarchy.hyprland.enable && kbAutoSwitch) keyboardWatch;
|
||||
}
|
||||
|
||||
@@ -5,14 +5,51 @@
|
||||
#
|
||||
# The hyprsunset HM service module is provided by home-manager; this only
|
||||
# configures it. Override anything with plain services.hyprsunset.* options.
|
||||
{ config, lib, ... }:
|
||||
#
|
||||
# nomarchy-nightlight is the menu/Waybar toggle: it starts/stops the hyprsunset
|
||||
# *service* (so `systemctl --user is-active` is the single source of truth and
|
||||
# the screen restores to true colours when stopped), letting you force the
|
||||
# filter off for colour-sensitive work and back on without touching the config.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.nightlight;
|
||||
|
||||
# Toggle/status helper, on PATH so the menu and both the generated and
|
||||
# whole-swap Waybars can drive it by name. Self-gates: when the unit doesn't
|
||||
# exist (night-light not enabled) `status` prints nothing, so the Waybar
|
||||
# module hides itself — same pattern as the power-profile indicator.
|
||||
nomarchy-nightlight = pkgs.writeShellScriptBin "nomarchy-nightlight" ''
|
||||
unit=hyprsunset.service
|
||||
if ! systemctl --user cat "$unit" >/dev/null 2>&1; then
|
||||
[ "''${1:-}" = status ] && exit 0
|
||||
notify-send "Night light" "Not enabled — set nomarchy.nightlight.enable."
|
||||
exit 0
|
||||
fi
|
||||
case "''${1:-toggle}" in
|
||||
status)
|
||||
if systemctl --user is-active --quiet "$unit"; then
|
||||
printf '{"text":"","tooltip":"Night light on — warm on schedule (click to disable)","class":"on"}\n'
|
||||
else
|
||||
printf '{"text":"","tooltip":"Night light off (click to enable)","class":"off"}\n'
|
||||
fi ;;
|
||||
on) systemctl --user start "$unit" ;;
|
||||
off) systemctl --user stop "$unit" ;;
|
||||
toggle)
|
||||
if systemctl --user is-active --quiet "$unit"; then
|
||||
systemctl --user stop "$unit"
|
||||
notify-send "Night light off" "Screen back to true colours."
|
||||
else
|
||||
systemctl --user start "$unit"
|
||||
notify-send "Night light on" "Warm filter follows your schedule."
|
||||
fi ;;
|
||||
*) echo "usage: nomarchy-nightlight [toggle|status|on|off]" >&2; exit 64 ;;
|
||||
esac
|
||||
'';
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.hyprsunset = {
|
||||
config = {
|
||||
services.hyprsunset = lib.mkIf cfg.enable {
|
||||
enable = true;
|
||||
settings.profile = [
|
||||
# Daytime: identity = no colour change.
|
||||
@@ -21,5 +58,9 @@ in
|
||||
{ time = cfg.sunset; temperature = cfg.temperature; }
|
||||
];
|
||||
};
|
||||
|
||||
# Always on PATH (it self-gates at runtime), so a static whole-swap Waybar
|
||||
# can exec it even when night-light is off without the call failing.
|
||||
home.packages = [ nomarchy-nightlight ];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -105,10 +105,11 @@ in
|
||||
picker. When non-empty, a small watcher runs in the session: when a
|
||||
keyboard connects that isn't covered by nomarchy.keyboard.devices and
|
||||
hasn't been chosen before, it pops a rofi picker (these layouts plus
|
||||
the primary nomarchy.keyboard.layout), applies the choice, and
|
||||
remembers it per-device (~/.local/state) — re-applying automatically
|
||||
on later reconnects. The runtime-remember complement to the
|
||||
declarative keyboard.devices; a stateful runtime piece by design.
|
||||
the primary nomarchy.keyboard.layout), applies the choice to that
|
||||
keyboard only (a per-device kb_layout, so the built-in board is left
|
||||
alone), and remembers it per-device (~/.local/state) — re-applying
|
||||
automatically on later reconnects. The runtime-remember complement to
|
||||
the declarative keyboard.devices; a stateful runtime piece by design.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -184,6 +185,32 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
updates = {
|
||||
enable = lib.mkEnableOption ''
|
||||
passive update awareness: a background check (systemd user timer) that
|
||||
compares the flake's locked inputs (nixpkgs, the Nomarchy input, …)
|
||||
against upstream and — when Flatpak is enabled — counts Flatpak
|
||||
updates, surfacing a Waybar indicator + a notification when something
|
||||
is available. It never changes anything; you still run sys-update /
|
||||
home-update / flatpak update yourself'';
|
||||
|
||||
interval = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "daily";
|
||||
example = "6h";
|
||||
description = "How often to check, as a systemd OnCalendar expression.";
|
||||
};
|
||||
|
||||
flatpak = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Also count available Flatpak updates when the `flatpak` CLI is
|
||||
present (i.e. nomarchy.services.flatpak is on). No effect otherwise.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
monitors = lib.mkOption {
|
||||
type = lib.types.listOf monitorType;
|
||||
default = [ ];
|
||||
|
||||
@@ -19,6 +19,92 @@ let
|
||||
rasiOverride = cfg.themesDir + "/${t.slug}/rofi.rasi";
|
||||
hasRasiOverride = builtins.pathExists rasiOverride;
|
||||
|
||||
# ── Visual theme picker ──────────────────────────────────────────────
|
||||
# Every preset (themes/<slug>.json) is read at eval time to build a grid
|
||||
# of real desktop previews. Grouped dark-first then light — the previews
|
||||
# make the mode obvious at a glance, so no light/dark submenu split — and
|
||||
# the active theme is marked ✓. "Active" is just t.slug: every switch
|
||||
# rebuilds this script, so the baked-in mark always tracks the live theme.
|
||||
themeFiles = lib.filterAttrs
|
||||
(n: ty: ty == "regular" && lib.hasSuffix ".json" n)
|
||||
(builtins.readDir cfg.themesDir);
|
||||
themeData = lib.mapAttrsToList (fname: _:
|
||||
let
|
||||
j = builtins.fromJSON (builtins.readFile (cfg.themesDir + "/${fname}"));
|
||||
slug = j.slug or (lib.removeSuffix ".json" fname);
|
||||
in {
|
||||
inherit slug;
|
||||
name = j.name or slug;
|
||||
mode = j.mode or "dark";
|
||||
hasPreview = builtins.pathExists (cfg.themesDir + "/${slug}/preview.png");
|
||||
}) themeFiles;
|
||||
|
||||
byName = lib.sort (a: b: a.name < b.name);
|
||||
orderedThemes =
|
||||
byName (lib.filter (th: th.mode != "light") themeData) # dark group
|
||||
++ byName (lib.filter (th: th.mode == "light") themeData); # then light
|
||||
|
||||
# rofi's element-icon cells are SQUARE, so a 16:9 preview would letterbox
|
||||
# (theme-coloured bands top/bottom). Build-time crop each committed 480×270
|
||||
# preview to a centred square so it fills the cell edge-to-edge. The source
|
||||
# preview.png stays 480×270 (untouched) — only the displayed thumb is square.
|
||||
themeThumbs = pkgs.runCommand "nomarchy-theme-thumbs"
|
||||
{ nativeBuildInputs = [ pkgs.imagemagick ]; } ''
|
||||
mkdir -p $out
|
||||
${lib.concatMapStringsSep "\n"
|
||||
(th: lib.optionalString th.hasPreview ''
|
||||
magick ${cfg.themesDir + "/${th.slug}/preview.png"} \
|
||||
-resize 360x360^ -gravity center -extent 360x360 -strip $out/${th.slug}.png
|
||||
'')
|
||||
orderedThemes}
|
||||
'';
|
||||
|
||||
# One grid row per theme (square thumb + name, ✓ on the active one), and a
|
||||
# Name→slug map so the picked label resolves back to the preset that
|
||||
# nomarchy-theme-sync applies (pretty names ≠ slugs, hence the map). Themes
|
||||
# with no preview degrade to a plain-name row.
|
||||
themeRows = lib.concatMapStringsSep "\n" (th:
|
||||
let label = th.name + lib.optionalString (th.slug == t.slug) " ✓";
|
||||
in if th.hasPreview
|
||||
then "row ${lib.escapeShellArg label} ${themeThumbs}/${th.slug}.png"
|
||||
else "printf '%s\\n' ${lib.escapeShellArg label}")
|
||||
orderedThemes;
|
||||
themeSlugMap = lib.concatMapStringsSep "\n" (th:
|
||||
" [${lib.escapeShellArg th.name}]=${lib.escapeShellArg th.slug}")
|
||||
orderedThemes;
|
||||
|
||||
# Per-invocation grid layout (cards: a 16:9 preview above a centered name),
|
||||
# overriding the single-column list theme just for this menu.
|
||||
# · The icon box is sized 16:9 (rofi 2.0 takes a two-value `size`) to
|
||||
# match the 480×270 previews exactly — they fill it with no letterbox.
|
||||
# · The window is sized to the *content* (3 × icon + paddings), not a
|
||||
# percentage, so a column is no wider than its card — otherwise the
|
||||
# selection highlight balloons out around the image.
|
||||
# · flow: horizontal lays cards out row-by-row (rofi's default Vertical
|
||||
# fills column-by-column, so Down at a column's foot jumped to the top
|
||||
# of the next column instead of scrolling the page).
|
||||
# Grid dial — the previews are as big as the icon px allows; screen *height*
|
||||
# caps it, so showing 2 rows (not 3) lets each card grow a lot. Scrolling
|
||||
# reaches the rest. Tune themeGridIconW for size; the window resizes to fit.
|
||||
# rofi's element-icon `size` is a SINGLE value (the manual only documents
|
||||
# one) — a two-value "WxH" is silently collapsed, which is why the preview
|
||||
# used to render tiny inside a too-wide column. So: size = the card *width*,
|
||||
# rofi fits the 16:9 image tight to it (no square letterbox), and the window
|
||||
# is sized so a column is exactly that width — the preview fills the cell.
|
||||
themeGridCols = 3;
|
||||
themeGridLines = 3;
|
||||
themeGridIconW = 240; # square preview card side (px) — the size knob
|
||||
themeGridPad = 0; # margin around the preview inside its cell (px)
|
||||
themeGridGap = 8; # gap between cards (px)
|
||||
themeGridThemeStr = lib.escapeShellArg (lib.concatStringsSep " " [
|
||||
# window width = cards + their padding + inter-card gaps + chrome (~20px)
|
||||
"window { width: ${toString (themeGridCols * (themeGridIconW + 2 * themeGridPad) + (themeGridCols - 1) * themeGridGap + 20)}px; }"
|
||||
"listview { columns: ${toString themeGridCols}; lines: ${toString themeGridLines}; spacing: ${toString themeGridGap}px; flow: horizontal; }"
|
||||
"element { orientation: vertical; padding: ${toString themeGridPad}px; spacing: 2px; }"
|
||||
"element-icon { size: ${toString themeGridIconW}px; }"
|
||||
"element-text { horizontal-align: 0.5; }"
|
||||
]);
|
||||
|
||||
# Keybindings cheatsheet (SUPER+? → the `keybinds` menu module). Built
|
||||
# from the SAME ./keybinds.nix that hyprland.nix binds, so it can never
|
||||
# drift from the live shortcuts. "$mod" reads as SUPER; rows are padded
|
||||
@@ -64,6 +150,14 @@ let
|
||||
# rofi has show-icons (on globally; passed explicitly on the icon menus).
|
||||
row() { printf '%s\0icon\x1f%s\n' "$1" "$2"; }
|
||||
|
||||
# General menu convention: every list menu ends with a "↩ Back" entry that
|
||||
# returns one level up (so you never have to Esc out and reopen). `back`
|
||||
# emits it with an icon; plain pick-lists append the bare label instead.
|
||||
# Always matched EXACTLY ("↩ Back") so it can't collide with clipboard or
|
||||
# filename content. The arrow + go-previous icon read as "go back".
|
||||
BACK="↩ Back"
|
||||
back() { row "$BACK" go-previous; }
|
||||
|
||||
case "''${1:-}" in
|
||||
power)
|
||||
choice=$( {
|
||||
@@ -73,8 +167,10 @@ let
|
||||
row "Hibernate" system-hibernate
|
||||
row "Reboot" system-reboot
|
||||
row "Shutdown" system-shutdown
|
||||
back
|
||||
} | rofi -dmenu -show-icons -p Power) || exit 0
|
||||
case "$choice" in
|
||||
"$BACK") exec "$0" ;;
|
||||
*Lock) loginctl lock-session ;;
|
||||
*Logout) hyprctl dispatch exit ;;
|
||||
*Suspend) systemctl suspend ;;
|
||||
@@ -92,17 +188,30 @@ let
|
||||
exit 0
|
||||
fi
|
||||
cur=$(powerprofilesctl get 2>/dev/null)
|
||||
choice=$(powerprofilesctl list 2>/dev/null \
|
||||
| sed -nE 's/^[* ] ([a-z-]+):$/\1/p' \
|
||||
choice=$( { powerprofilesctl list 2>/dev/null | sed -nE 's/^[* ] ([a-z-]+):$/\1/p'; printf '%s\n' "$BACK"; } \
|
||||
| rofi -dmenu -p "profile (now: $cur)") || exit 0
|
||||
[ "$choice" = "$BACK" ] && exec "$0" system
|
||||
[ -n "$choice" ] && powerprofilesctl set "$choice" ;;
|
||||
|
||||
theme)
|
||||
choice=$(nomarchy-theme-sync list | rofi -dmenu -p theme) || exit 0
|
||||
[ -n "$choice" ] && exec nomarchy-theme-sync apply "$choice" ;;
|
||||
# Visual picker: a grid of real desktop previews (rows + Name→slug
|
||||
# map generated from the presets at build time). Pick a card → apply
|
||||
# that slug. The grid layout is a per-invocation -theme-str override.
|
||||
declare -A THEME_SLUG=(
|
||||
${themeSlugMap}
|
||||
)
|
||||
choice=$( {
|
||||
${themeRows}
|
||||
back
|
||||
} | rofi -dmenu -show-icons -p Theme -theme-str ${themeGridThemeStr}) || exit 0
|
||||
[ "$choice" = "$BACK" ] && exec "$0"
|
||||
choice="''${choice% ✓}" # drop the active marker if present
|
||||
slug="''${THEME_SLUG[$choice]:-}"
|
||||
[ -n "$slug" ] && exec nomarchy-theme-sync apply "$slug" ;;
|
||||
|
||||
clipboard)
|
||||
sel=$(cliphist list | rofi -dmenu -p clip) || exit 0
|
||||
sel=$( { cliphist list; printf '%s\n' "$BACK"; } | rofi -dmenu -p clip) || exit 0
|
||||
[ "$sel" = "$BACK" ] && exec "$0" tools
|
||||
printf '%s' "$sel" | cliphist decode | wl-copy ;;
|
||||
|
||||
calc)
|
||||
@@ -114,10 +223,10 @@ let
|
||||
-calc-command "echo -n '{result}' | wl-copy" ;;
|
||||
|
||||
files)
|
||||
sel=$(fd . "$HOME" --type f --hidden --exclude .git --exclude .cache 2>/dev/null \
|
||||
| head -n 50000 \
|
||||
| sed "s|^$HOME/||" \
|
||||
sel=$( { fd . "$HOME" --type f --hidden --exclude .git --exclude .cache 2>/dev/null \
|
||||
| head -n 50000 | sed "s|^$HOME/||"; printf '%s\n' "$BACK"; } \
|
||||
| rofi -dmenu -p file) || exit 0
|
||||
[ "$sel" = "$BACK" ] && exec "$0" tools
|
||||
[ -n "$sel" ] && exec xdg-open "$HOME/$sel" ;;
|
||||
|
||||
emoji)
|
||||
@@ -145,10 +254,12 @@ let
|
||||
row "Region → file" applets-screenshooter
|
||||
row "Full screen → clipboard" camera-photo
|
||||
row "Full screen → file" camera-photo
|
||||
back
|
||||
} | rofi -dmenu -show-icons -p Capture) || exit 0
|
||||
dir="$HOME/Pictures/Screenshots"
|
||||
file="$dir/$(date +%Y%m%d-%H%M%S).png"
|
||||
case "$choice" in
|
||||
"$BACK") exec "$0" tools ;;
|
||||
*"Region → clipboard"*) grim -g "$(slurp)" - | wl-copy ;;
|
||||
*"Region → file"*) mkdir -p "$dir"; grim -g "$(slurp)" "$file" \
|
||||
&& notify-send "Screenshot saved" "$file" ;;
|
||||
@@ -178,13 +289,23 @@ let
|
||||
&& notify-send "Do Not Disturb off" "Notifications resumed."
|
||||
exit 0 ;;
|
||||
|
||||
nightlight)
|
||||
# Force the scheduled blue-light filter on/off for the session by
|
||||
# starting/stopping hyprsunset (nomarchy-nightlight, from nightlight.nix).
|
||||
# Self-gated in the menu; guard here too in case it's invoked directly.
|
||||
command -v nomarchy-nightlight >/dev/null 2>&1 \
|
||||
&& exec nomarchy-nightlight toggle
|
||||
notify-send "Night light" "Not available (nomarchy.nightlight off?)."; exit 0 ;;
|
||||
|
||||
snapshot)
|
||||
# btrfs-assistant: snapshot browse / diff / restore / rollback over
|
||||
# snapper, elevating via polkit. Shipped system-side with
|
||||
# nomarchy.system.snapper; self-gate so the entry no-ops elsewhere.
|
||||
command -v btrfs-assistant >/dev/null 2>&1 \
|
||||
|| { notify-send "Snapshots" "btrfs-assistant isn't installed (BTRFS snapshots off?)."; exit 0; }
|
||||
exec btrfs-assistant ;;
|
||||
# btrfs-assistant 2.2 segfaults on 26.05 (libbtrfsutil ABI), so launch
|
||||
# the keyboard-driven snapper browser instead: a terminal running it
|
||||
# via sudo (snapper is root-only here — one password prompt), with
|
||||
# browse/diff, restore (undochange) and rollback, each confirmed.
|
||||
# Self-gated to nomarchy.system.snapper, so it no-ops elsewhere.
|
||||
command -v nomarchy-snapshots >/dev/null 2>&1 \
|
||||
|| { notify-send "Snapshots" "Snapshot tools unavailable (nomarchy.system.snapper off?)."; exit 0; }
|
||||
exec ${cfg.terminal} -e sudo nomarchy-snapshots ;;
|
||||
|
||||
tools)
|
||||
# Tools submenu — utilities you invoke. Each leaf is also reachable
|
||||
@@ -197,9 +318,10 @@ let
|
||||
row "Web search" system-search
|
||||
row "Capture" applets-screenshooter
|
||||
row "Ask Claude" internet-chat
|
||||
row "Back" go-previous
|
||||
back
|
||||
} | rofi -dmenu -show-icons -p Tools) || exit 0
|
||||
case "$choice" in
|
||||
"$BACK") exec "$0" ;;
|
||||
*Calc*) exec "$0" calc ;;
|
||||
*Clipboard*) exec "$0" clipboard ;;
|
||||
*Emoji*) exec "$0" emoji ;;
|
||||
@@ -207,7 +329,6 @@ let
|
||||
*Web*) exec "$0" web ;;
|
||||
*Capture*) exec "$0" capture ;;
|
||||
*Ask*) exec "$0" ask ;;
|
||||
*Back*) exec "$0" ;;
|
||||
esac ;;
|
||||
|
||||
system)
|
||||
@@ -219,19 +340,22 @@ let
|
||||
row "Network" network-wireless
|
||||
row "Bluetooth" bluetooth
|
||||
row "Do Not Disturb" notification-disabled
|
||||
command -v btrfs-assistant >/dev/null 2>&1 && row "Snapshots" timeshift
|
||||
systemctl --user cat hyprsunset.service >/dev/null 2>&1 \
|
||||
&& row "Night light" weather-clear-night
|
||||
command -v nomarchy-snapshots >/dev/null 2>&1 && row "Snapshots" timeshift
|
||||
if [ -e "''${bats[0]}" ] && command -v powerprofilesctl >/dev/null 2>&1; then
|
||||
row "Power profile" preferences-system-power
|
||||
fi
|
||||
row "Back" go-previous
|
||||
back
|
||||
} | rofi -dmenu -show-icons -p System) || exit 0
|
||||
case "$choice" in
|
||||
"$BACK") exec "$0" ;;
|
||||
*Network*) exec "$0" network ;;
|
||||
*Bluetooth*) exec "$0" bluetooth ;;
|
||||
*"Do Not Disturb"*) exec "$0" dnd ;;
|
||||
*"Night light"*) exec "$0" nightlight ;;
|
||||
*Snapshots*) exec "$0" snapshot ;;
|
||||
*"Power profile"*) exec "$0" power-profile ;;
|
||||
*Back*) exec "$0" ;;
|
||||
esac ;;
|
||||
|
||||
"")
|
||||
@@ -256,7 +380,7 @@ let
|
||||
esac ;;
|
||||
|
||||
*)
|
||||
echo "usage: nomarchy-menu [tools|system|power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|capture|keybinds|ask|dnd|snapshot]" >&2
|
||||
echo "usage: nomarchy-menu [tools|system|power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|capture|keybinds|ask|dnd|nightlight|snapshot]" >&2
|
||||
exit 64 ;;
|
||||
esac
|
||||
'';
|
||||
|
||||
141
modules/home/updates.nix
Normal file
@@ -0,0 +1,141 @@
|
||||
# Update awareness (opt-in, nomarchy.updates.enable) — a passive background
|
||||
# check that surfaces a Waybar indicator + a notification when updates are
|
||||
# available, without ever changing anything (you still run sys-update /
|
||||
# home-update / flatpak update). It counts:
|
||||
# • flake inputs whose locked rev is behind upstream (nixpkgs, the Nomarchy
|
||||
# input, home-manager …) — via `git ls-remote` on each branch-tracking
|
||||
# github/git input in flake.lock; offline → skipped, never a false alarm.
|
||||
# • Flatpak updates, when the `flatpak` CLI is present (services.flatpak on).
|
||||
#
|
||||
# nomarchy-updates is always on PATH and self-gates (status prints nothing
|
||||
# until the timer has found something), so the Waybar module — generated and
|
||||
# whole-swap — can exec it by name even when the feature is off.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.updates;
|
||||
|
||||
nomarchy-updates = pkgs.writeShellScriptBin "nomarchy-updates" ''
|
||||
set -u
|
||||
# System + user profiles, so flatpak / sys-update resolve from a timer-run
|
||||
# service too (build-time tools below use absolute store paths regardless).
|
||||
export PATH="$PATH:/run/current-system/sw/bin:/etc/profiles/per-user/$USER/bin"
|
||||
GIT=${pkgs.git}/bin/git
|
||||
JQ=${pkgs.jq}/bin/jq
|
||||
cache="''${XDG_CACHE_HOME:-$HOME/.cache}/nomarchy"
|
||||
state="$cache/updates.json"
|
||||
flake="''${NOMARCHY_PATH:-$HOME/.nomarchy}"
|
||||
mkdir -p "$cache"
|
||||
|
||||
count_nix() {
|
||||
local lock="$flake/flake.lock" n=0 name kind owner repo url ref locked giturl up
|
||||
[ -f "$lock" ] || { echo 0; return; }
|
||||
while IFS=$'\t' read -r name kind owner repo url ref locked; do
|
||||
[ -n "$locked" ] || continue
|
||||
case "$kind" in
|
||||
github) giturl="https://github.com/$owner/$repo" ;;
|
||||
git) giturl="$url" ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
up=$("$GIT" ls-remote "$giturl" "$ref" 2>/dev/null | ${pkgs.gawk}/bin/awk 'NR==1{print $1}')
|
||||
[ -n "$up" ] || continue # offline / unknown → skip (no false alarm)
|
||||
[ "$up" != "$locked" ] && n=$((n + 1))
|
||||
done < <(
|
||||
# Only the flake's DIRECT inputs (root.inputs) — not the transitive
|
||||
# closure — so a deep dependency bump doesn't nag as an "update".
|
||||
"$JQ" -r '
|
||||
.nodes as $nodes
|
||||
| ($nodes.root.inputs | [ .[] | if type == "array" then .[0] else . end ]) as $direct
|
||||
| $nodes | to_entries[]
|
||||
| .key as $k | .value as $v
|
||||
| select($direct | index($k))
|
||||
| select(($v.original.type? == "github") or ($v.original.type? == "git"))
|
||||
| select(($v.original.rev? // "") == "") # branch-tracking only
|
||||
| [ $k, $v.original.type,
|
||||
($v.original.owner? // ""), ($v.original.repo? // ""),
|
||||
($v.original.url? // ""), ($v.original.ref? // "HEAD"),
|
||||
($v.locked.rev? // "") ] | @tsv
|
||||
' "$lock"
|
||||
)
|
||||
echo "$n"
|
||||
}
|
||||
|
||||
count_flatpak() {
|
||||
${lib.optionalString cfg.flatpak ''
|
||||
if command -v flatpak >/dev/null 2>&1; then
|
||||
flatpak remote-ls --updates --columns=application 2>/dev/null | ${pkgs.gnugrep}/bin/grep -c . || true
|
||||
return
|
||||
fi
|
||||
''}
|
||||
echo 0
|
||||
}
|
||||
|
||||
refresh_bar() { ${pkgs.procps}/bin/pkill -RTMIN+9 -x waybar 2>/dev/null || true; }
|
||||
|
||||
case "''${1:-status}" in
|
||||
check)
|
||||
nix=$(count_nix); fp=$(count_flatpak); total=$((nix + fp))
|
||||
prev=$("$JQ" -r '.total // 0' "$state" 2>/dev/null || echo 0)
|
||||
printf '{"nix":%d,"flatpak":%d,"total":%d,"ts":%d}\n' \
|
||||
"$nix" "$fp" "$total" "$(${pkgs.coreutils}/bin/date +%s)" > "$state"
|
||||
# Notify only when NEW updates appear, so a daily timer doesn't nag.
|
||||
if [ "$total" -gt 0 ] && [ "$total" -gt "$prev" ]; then
|
||||
msg="$nix flake input(s)"
|
||||
[ "$fp" -gt 0 ] && msg="$msg · $fp Flatpak(s)"
|
||||
${pkgs.libnotify}/bin/notify-send -a Nomarchy "Updates available" \
|
||||
"$msg — click the bar icon, or run sys-update."
|
||||
fi
|
||||
refresh_bar ;;
|
||||
status)
|
||||
total=$("$JQ" -r '.total // 0' "$state" 2>/dev/null || echo 0)
|
||||
[ "$total" -gt 0 ] 2>/dev/null || exit 0 # up to date / unchecked → hide
|
||||
nix=$("$JQ" -r '.nix // 0' "$state"); fp=$("$JQ" -r '.flatpak // 0' "$state")
|
||||
tip="Updates available"
|
||||
[ "$nix" -gt 0 ] && tip="$tip\n• $nix flake input(s) — sys-update"
|
||||
[ "$fp" -gt 0 ] && tip="$tip\n• $fp Flatpak(s) — flatpak update"
|
||||
printf '{"text":" %d","tooltip":"%s","class":"available"}\n' "$total" "$tip" ;;
|
||||
upgrade)
|
||||
echo "Checking…"; "$0" check
|
||||
nix=$("$JQ" -r '.nix // 0' "$state" 2>/dev/null || echo 0)
|
||||
fp=$("$JQ" -r '.flatpak // 0' "$state" 2>/dev/null || echo 0)
|
||||
echo "Pending: $nix flake input(s), $fp Flatpak(s)."
|
||||
if [ "$nix" -gt 0 ] && command -v sys-update >/dev/null 2>&1; then
|
||||
read -rp "Run sys-update (flake update + system rebuild)? [y/N] " a
|
||||
[ "$a" = y ] && sys-update
|
||||
fi
|
||||
if [ "$fp" -gt 0 ] && command -v flatpak >/dev/null 2>&1; then
|
||||
read -rp "Run flatpak update? [y/N] " a
|
||||
[ "$a" = y ] && flatpak update
|
||||
fi
|
||||
"$0" check
|
||||
echo "Done — press enter."; read -r _ || true ;;
|
||||
*) echo "usage: nomarchy-updates [check|status|upgrade]" >&2; exit 64 ;;
|
||||
esac
|
||||
'';
|
||||
in
|
||||
{
|
||||
config = lib.mkMerge [
|
||||
# Always on PATH so the Waybar module (incl. the static whole-swap themes)
|
||||
# can exec it; it self-gates at runtime.
|
||||
{ home.packages = [ nomarchy-updates ]; }
|
||||
|
||||
(lib.mkIf cfg.enable {
|
||||
systemd.user.services.nomarchy-updates = {
|
||||
Unit.Description = "Check for Nomarchy / nixpkgs / Flatpak updates";
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${nomarchy-updates}/bin/nomarchy-updates check";
|
||||
};
|
||||
};
|
||||
systemd.user.timers.nomarchy-updates = {
|
||||
Unit.Description = "Periodic update-awareness check";
|
||||
Timer = {
|
||||
OnStartupSec = "2min";
|
||||
OnCalendar = cfg.interval;
|
||||
Persistent = true;
|
||||
};
|
||||
Install.WantedBy = [ "timers.target" ];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
@@ -77,9 +77,9 @@ let
|
||||
|
||||
modules-left = [ "hyprland/workspaces" "hyprland/window" ];
|
||||
modules-center = [ "clock" ];
|
||||
modules-right = [ "tray" "pulseaudio" "network" "cpu" "memory" "custom/powerprofile" ]
|
||||
modules-right = [ "tray" "pulseaudio" "cpu" "memory" "custom/powerprofile" "custom/nightlight" ]
|
||||
++ lib.optional showLanguage "hyprland/language"
|
||||
++ [ "battery" "custom/notification" ];
|
||||
++ [ "battery" "custom/updates" "custom/notification" ];
|
||||
|
||||
"hyprland/workspaces" = {
|
||||
format = "{icon}";
|
||||
@@ -111,15 +111,8 @@ let
|
||||
on-click = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
||||
};
|
||||
|
||||
network = {
|
||||
format-wifi = " {essid}";
|
||||
format-ethernet = "";
|
||||
format-disconnected = "";
|
||||
tooltip-format = "{ipaddr} via {gwaddr}";
|
||||
# nm-applet sits in the tray for the GUI path; this is the
|
||||
# keyboard-friendly one.
|
||||
on-click = "${config.nomarchy.terminal} -e nmtui";
|
||||
};
|
||||
# No network module: nm-applet lives in the tray (the GUI path), so the
|
||||
# bar's wifi/ethernet indicator would just duplicate it.
|
||||
|
||||
cpu.format = " {usage}%";
|
||||
memory.format = " {percentage}%";
|
||||
@@ -138,6 +131,28 @@ let
|
||||
on-click = "nomarchy-powerprofile-cycle";
|
||||
};
|
||||
|
||||
# Night-light (hyprsunset) toggle + state. Self-gates: hidden unless
|
||||
# nomarchy.nightlight is enabled (the status helper prints nothing then).
|
||||
# Click starts/stops the schedule; moon = on, sun = off.
|
||||
"custom/nightlight" = {
|
||||
exec = "nomarchy-nightlight status";
|
||||
return-type = "json";
|
||||
interval = 3;
|
||||
on-click = "nomarchy-nightlight toggle";
|
||||
};
|
||||
|
||||
# Update awareness. Self-gates: hidden unless nomarchy.updates is enabled
|
||||
# AND the periodic check found something (the helper prints nothing then).
|
||||
# signal 9 lets the checker refresh it instantly; click opens the upgrade
|
||||
# flow in a terminal.
|
||||
"custom/updates" = {
|
||||
exec = "nomarchy-updates status";
|
||||
return-type = "json";
|
||||
interval = 1800;
|
||||
signal = 9;
|
||||
on-click = "${config.nomarchy.terminal} -e nomarchy-updates upgrade";
|
||||
};
|
||||
|
||||
# swaync notification bell + Do-Not-Disturb state. `-swb` streams JSON
|
||||
# (text/tooltip/class) on every change, so it tracks count and DND with
|
||||
# no polling. Left-click toggles the panel; right-click toggles DND.
|
||||
@@ -209,11 +224,21 @@ let
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#tray, #pulseaudio, #network, #cpu, #memory, #custom-powerprofile, #language, #battery, #custom-notification {
|
||||
#tray, #pulseaudio, #cpu, #memory, #custom-powerprofile, #custom-nightlight, #custom-updates, #language, #battery, #custom-notification {
|
||||
color: @subtext;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* Night-light active → warm tone, matching the filter it represents. */
|
||||
#custom-nightlight.on { color: @warn; }
|
||||
|
||||
/* Updates pending → accent, to draw the eye. */
|
||||
#custom-updates.available { color: @accent; }
|
||||
|
||||
/* The power-profile speedometer glyph renders small in its em box —
|
||||
size it up so it reads at a glance like the other indicators. */
|
||||
#custom-powerprofile { font-size: ${toString (t.fonts.size + 3)}pt; }
|
||||
|
||||
/* notifications waiting → accent; Do-Not-Disturb → muted bell-off */
|
||||
#custom-notification.notification { color: @accent; }
|
||||
#custom-notification.dnd-none,
|
||||
@@ -228,7 +253,14 @@ in
|
||||
{
|
||||
programs.waybar = lib.mkIf config.nomarchy.waybar.enable {
|
||||
enable = true;
|
||||
systemd.enable = true; # started/stopped with graphical-session.target
|
||||
# Launched from Hyprland's exec-once (hyprland.nix), NOT a systemd user
|
||||
# service. Bound to graphical-session.target the unit raced Hyprland's IPC
|
||||
# on a warm relogin — it started before the socket was up, exited, landed
|
||||
# in `failed`, and was never retried, so the bar vanished. exec-once only
|
||||
# fires once Hyprland is up, dodging the race; theme switches reload the
|
||||
# running bar via SIGUSR2 (nomarchy-theme-sync). No uwsm here to manage the
|
||||
# session target, so we don't depend on its lifecycle.
|
||||
systemd.enable = false;
|
||||
|
||||
# mkDefault so downstream can replace the whole bar config/style with
|
||||
# a plain home.nix assignment. For per-theme identity, prefer the
|
||||
|
||||
@@ -22,7 +22,7 @@ let
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [ ./options.nix ./plymouth.nix ./file-manager.nix ./power.nix ./services.nix ];
|
||||
imports = [ ./options.nix ./plymouth.nix ./file-manager.nix ./power.nix ./services.nix ./hardware.nix ];
|
||||
|
||||
config = {
|
||||
# The safe half of distro branding: distroName flows into
|
||||
@@ -69,6 +69,11 @@ in
|
||||
console.earlySetup = lib.mkDefault true;
|
||||
boot.initrd.systemd.enable = lib.mkDefault true;
|
||||
|
||||
# Nomarchy roots are BTRFS, not ZFS, so adopt the 26.11 default early and
|
||||
# silence the eval warning the old `true` default emits. mkDefault, so a
|
||||
# genuine ZFS-root downstream can still force it back on.
|
||||
boot.zfs.forceImportRoot = lib.mkDefault false;
|
||||
|
||||
# ── Wayland session: Hyprland ────────────────────────────────────
|
||||
# Installs the binary, registers the session, wires up
|
||||
# xdg-desktop-portal-hyprland. Configuration is Home Manager's job.
|
||||
@@ -320,8 +325,69 @@ in
|
||||
nixos-rebuild switch --flake /etc/nixos#default "$@"
|
||||
'')
|
||||
# The desktop snapshot manager (browse / diff / restore / rollback over
|
||||
# snapper, elevating via polkit) — what `nomarchy-menu snapshot` launches.
|
||||
++ lib.optional cfg.snapper.enable pkgs.btrfs-assistant;
|
||||
# snapper, elevating via polkit). Kept for when nixpkgs fixes its crash.
|
||||
++ lib.optional cfg.snapper.enable pkgs.btrfs-assistant
|
||||
# Keyboard-driven snapper browser/restore — the fallback `nomarchy-menu
|
||||
# snapshot` actually launches while btrfs-assistant 2.2 segfaults on 26.05
|
||||
# (libbtrfsutil ABI). Runs as root (snapper is root-only here; the menu
|
||||
# opens it in a terminal via sudo, one password prompt), fzf to pick, with
|
||||
# browse/diff (read-only) and typed-`yes` confirmation before any write.
|
||||
++ lib.optional cfg.snapper.enable (pkgs.writeShellApplication {
|
||||
name = "nomarchy-snapshots";
|
||||
runtimeInputs = with pkgs; [ snapper fzf gawk gnugrep less coreutils systemd ];
|
||||
text = ''
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "nomarchy-snapshots must run as root (snapper needs it) — use sudo." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mapfile -t configs < <(snapper list-configs | awk 'NR>2 {print $1}' | grep .)
|
||||
if [ "''${#configs[@]}" -eq 0 ]; then
|
||||
echo "No snapper configs found." >&2; exit 1
|
||||
elif [ "''${#configs[@]}" -eq 1 ]; then
|
||||
config="''${configs[0]}"
|
||||
else
|
||||
config=$(printf '%s\n' "''${configs[@]}" | fzf --prompt="snapper config> ") || exit 0
|
||||
fi
|
||||
|
||||
while :; do
|
||||
snap=$(snapper -c "$config" list \
|
||||
| fzf --header-lines=2 --prompt="[$config] pick a snapshot (Esc quits)> ") || exit 0
|
||||
num=$(awk '{print $1}' <<<"$snap")
|
||||
case "$num" in ""|*[!0-9]*) continue ;; esac
|
||||
|
||||
action=$(printf '%s\n' \
|
||||
"Browse changes since #$num (read-only)" \
|
||||
"Restore changed files to #$num (undochange)" \
|
||||
"Roll the system back to #$num (reboot)" \
|
||||
"↩ Back to the snapshot list" \
|
||||
| fzf --prompt="snapshot #$num> ") || exit 0
|
||||
|
||||
case "$action" in
|
||||
Browse*)
|
||||
snapper -c "$config" status "$num..0" | less -R || true ;;
|
||||
Restore*)
|
||||
read -rp "Revert files in '$config' to snapshot #$num? Type yes to confirm: " ans || continue
|
||||
if [ "$ans" = yes ]; then
|
||||
snapper -c "$config" undochange "$num..0"
|
||||
echo "Files restored. Press enter."; read -r _ || true
|
||||
fi ;;
|
||||
Roll*)
|
||||
if [ "$config" != root ]; then
|
||||
echo "Rollback applies to the 'root' config only; use Restore for '$config'. Press enter."
|
||||
read -r _ || true
|
||||
else
|
||||
read -rp "Roll the SYSTEM back to #$num and REBOOT now? Type yes to confirm: " ans || continue
|
||||
if [ "$ans" = yes ]; then
|
||||
snapper -c root rollback "$num"
|
||||
echo "Rolled back — rebooting…"; systemctl reboot
|
||||
fi
|
||||
fi ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
done
|
||||
'';
|
||||
});
|
||||
|
||||
# Don't let boot entries fill the ESP over the years.
|
||||
boot.loader.systemd-boot.configurationLimit = lib.mkDefault 10;
|
||||
|
||||
195
modules/nixos/hardware.nix
Normal file
@@ -0,0 +1,195 @@
|
||||
# Hardware enablement beyond nixos-hardware.
|
||||
#
|
||||
# The nixos-hardware "common-*" profiles the installer selects cover the
|
||||
# BASICS (microcode, the Intel/AMD VA-API media stack, weekly fstrim), and
|
||||
# power.nix adds thermald + power-profiles-daemon. This module fills the GAP
|
||||
# above those: broadly-beneficial bits that default ON when the installer
|
||||
# detects the vendor (opt-OUT), and heavier/experimental bits behind opt-IN
|
||||
# toggles. The installer's hardware-db.sh probes what's present and writes the
|
||||
# matching nomarchy.hardware.* into the generated system.nix.
|
||||
#
|
||||
# Audited against the commons so we don't double-set: we add GuC/HuC, the
|
||||
# amd-pstate governor, the AMD VA-API env, GPU-compute runtimes, fprintd, and
|
||||
# the NPU driver — none of which the commons turn on.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.hardware;
|
||||
in
|
||||
{
|
||||
options.nomarchy.hardware = {
|
||||
intel = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Intel CPU/GPU enablement (the installer turns this on when it detects
|
||||
an Intel CPU or GPU). Complements nixos-hardware's common-gpu-intel'';
|
||||
|
||||
guc = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = cfg.intel.enable;
|
||||
defaultText = lib.literalExpression "config.nomarchy.hardware.intel.enable";
|
||||
description = ''
|
||||
Load the GPU's GuC/HuC firmware via the i915 param
|
||||
(i915.enable_guc=3) — better power management and HuC-accelerated
|
||||
media. On by default with intel.enable. NOTE: this is the *i915*
|
||||
driver's param; the newer `xe` driver (Lunar Lake / Battlemage /
|
||||
Panther Lake and other recent Xe GPUs) enables GuC by default and
|
||||
ignores it, so the installer turns this off on xe-driver hardware.
|
||||
'';
|
||||
};
|
||||
|
||||
computeRuntime = lib.mkEnableOption ''
|
||||
Intel GPU compute: the OpenCL / Level Zero (intel-compute-runtime) and
|
||||
oneVPL (vpl-gpu-rt) runtimes for GPU compute and transcode. Opt-in (a
|
||||
few hundred MB) — the Intel counterpart to AMD ROCm'';
|
||||
};
|
||||
|
||||
amd = {
|
||||
enable = lib.mkEnableOption ''
|
||||
AMD CPU/GPU enablement (installer-set on an AMD CPU or GPU).
|
||||
Complements nixos-hardware's common-cpu-amd / common-gpu-amd'';
|
||||
|
||||
pstate = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = cfg.amd.enable;
|
||||
defaultText = lib.literalExpression "config.nomarchy.hardware.amd.enable";
|
||||
description = ''
|
||||
Use the amd-pstate EPP driver (amd_pstate=active) — the modern Zen
|
||||
power/perf governor that power-profiles-daemon drives per profile.
|
||||
On by default with amd.enable (broadly beneficial on Zen 2+).
|
||||
'';
|
||||
};
|
||||
|
||||
vaapi = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = cfg.amd.enable;
|
||||
defaultText = lib.literalExpression "config.nomarchy.hardware.amd.enable";
|
||||
description = ''
|
||||
Point VA-API at mesa's radeonsi (LIBVA_DRIVER_NAME=radeonsi) for
|
||||
hardware video decode/encode. On by default with amd.enable.
|
||||
'';
|
||||
};
|
||||
|
||||
rocm = {
|
||||
enable = lib.mkEnableOption ''
|
||||
AMD ROCm: the HIP / OpenCL GPU-compute stack (multi-GB closure).
|
||||
Opt-in — unlocks GPU PyTorch / Ollama on Radeon'';
|
||||
|
||||
gfxOverride = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
example = "11.0.0";
|
||||
description = ''
|
||||
HSA_OVERRIDE_GFX_VERSION for GPUs ROCm doesn't officially list
|
||||
(e.g. an RDNA3 780M iGPU, gfx1103, needs "11.0.0"). Empty = no
|
||||
override.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fingerprint = {
|
||||
enable = lib.mkEnableOption ''
|
||||
a fingerprint reader via fprintd (the installer turns this on when it
|
||||
detects a known reader). Enroll with `fprintd-enroll`'';
|
||||
|
||||
pam = lib.mkEnableOption ''
|
||||
using the fingerprint for login and sudo (PAM). Opt-in — password-only
|
||||
stays the default for the cautious; enroll a finger first'';
|
||||
};
|
||||
|
||||
npu.enable = lib.mkEnableOption ''
|
||||
the on-die NPU (AI accelerator) kernel driver — amdxdna on AMD (Ryzen
|
||||
AI), intel_vpu on Intel (Core Ultra and newer). Opt-in and experimental:
|
||||
this loads the in-kernel driver only; the userspace runtime (AMD XRT /
|
||||
oneAPI Level Zero NPU) is yours to add. Needs a recent kernel (see
|
||||
latestKernel)'';
|
||||
|
||||
latestKernel = lib.mkEnableOption ''
|
||||
the latest mainline kernel (pkgs.linuxPackages_latest) instead of the
|
||||
distro default — for very new hardware whose drivers (a fresh NPU, the
|
||||
`xe` GPU driver, new-platform enablement) only landed recently. Off by
|
||||
default; the default kernel already carries amd-pstate and amdxdna (6.14+)'';
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
# ── Intel ──────────────────────────────────────────────────────────
|
||||
(lib.mkIf cfg.intel.guc {
|
||||
boot.kernelParams = [ "i915.enable_guc=3" ];
|
||||
})
|
||||
(lib.mkIf cfg.intel.computeRuntime {
|
||||
hardware.graphics.extraPackages = with pkgs; [ intel-compute-runtime vpl-gpu-rt ];
|
||||
})
|
||||
|
||||
# ── AMD ────────────────────────────────────────────────────────────
|
||||
(lib.mkIf cfg.amd.pstate {
|
||||
boot.kernelParams = [ "amd_pstate=active" ];
|
||||
})
|
||||
(lib.mkIf cfg.amd.vaapi {
|
||||
# radeonsi itself comes from mesa (via common-gpu-amd); this just steers
|
||||
# libva at it. mkDefault so a hand-set value or another module wins.
|
||||
environment.sessionVariables.LIBVA_DRIVER_NAME = lib.mkDefault "radeonsi";
|
||||
hardware.graphics.extraPackages = [ pkgs.libva ];
|
||||
})
|
||||
(lib.mkIf cfg.amd.rocm.enable {
|
||||
hardware.graphics.extraPackages = [ pkgs.rocmPackages.clr pkgs.rocmPackages.clr.icd ];
|
||||
environment.sessionVariables = lib.optionalAttrs (cfg.amd.rocm.gfxOverride != "") {
|
||||
HSA_OVERRIDE_GFX_VERSION = cfg.amd.rocm.gfxOverride;
|
||||
};
|
||||
})
|
||||
|
||||
# ── Fingerprint ────────────────────────────────────────────────────
|
||||
(lib.mkIf cfg.fingerprint.enable {
|
||||
services.fprintd.enable = true;
|
||||
})
|
||||
(lib.mkIf (cfg.fingerprint.enable && cfg.fingerprint.pam) {
|
||||
security.pam.services.login.fprintAuth = true;
|
||||
security.pam.services.sudo.fprintAuth = true;
|
||||
})
|
||||
|
||||
# ── Newest kernel for very-new hardware (opt-in escape hatch) ──────
|
||||
(lib.mkIf cfg.latestKernel {
|
||||
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
|
||||
})
|
||||
|
||||
# ── NPU (in-kernel driver only; userspace runtime is BYO) ──────────
|
||||
# The driver has to actually be in the running kernel — warn (don't fail)
|
||||
# when it predates the shipped one, pointing at latestKernel.
|
||||
(lib.mkIf (cfg.npu.enable && cfg.amd.enable) {
|
||||
boot.kernelModules = [ "amdxdna" ];
|
||||
warnings = lib.optional
|
||||
(!lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.14")
|
||||
''
|
||||
nomarchy.hardware.npu: the amdxdna driver needs kernel >= 6.14, but
|
||||
this config ships ${config.boot.kernelPackages.kernel.version}. Set
|
||||
nomarchy.hardware.latestKernel = true.'';
|
||||
})
|
||||
(lib.mkIf (cfg.npu.enable && cfg.intel.enable) {
|
||||
boot.kernelModules = [ "intel_vpu" ];
|
||||
warnings = lib.optional
|
||||
(!lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.11")
|
||||
''
|
||||
nomarchy.hardware.npu: the intel_vpu driver (especially for newer
|
||||
NPUs) wants a recent kernel, but this config ships
|
||||
${config.boot.kernelPackages.kernel.version}. Consider
|
||||
nomarchy.hardware.latestKernel = true.'';
|
||||
})
|
||||
|
||||
# ── Sanity ─────────────────────────────────────────────────────────
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.amd.rocm.enable -> cfg.amd.enable;
|
||||
message = "nomarchy.hardware.amd.rocm.enable needs nomarchy.hardware.amd.enable.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.intel.computeRuntime -> cfg.intel.enable;
|
||||
message = "nomarchy.hardware.intel.computeRuntime needs nomarchy.hardware.intel.enable.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.npu.enable -> (cfg.amd.enable || cfg.intel.enable);
|
||||
message = "nomarchy.hardware.npu.enable needs a detected Intel or AMD platform.";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -112,6 +112,7 @@ HARDWARE_DB=(
|
||||
# ----------------------------------------------------------------------------
|
||||
nomarchy_detect_hw() {
|
||||
local sys_vendor product_name cpu_vendor
|
||||
local nvidia=0 amdgpu=0 intelgpu=0
|
||||
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}')
|
||||
@@ -126,7 +127,7 @@ nomarchy_detect_hw() {
|
||||
|
||||
# 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
|
||||
local gpu_line
|
||||
while IFS= read -r gpu_line; do
|
||||
case "$gpu_line" in
|
||||
*"[10de:"*|*"NVIDIA"*) nvidia=1 ;;
|
||||
@@ -140,6 +141,67 @@ nomarchy_detect_hw() {
|
||||
(( intelgpu )) && { echo "MODULE common-gpu-intel"; echo "DETAIL gpu: Intel"; detected=1; }
|
||||
fi
|
||||
|
||||
# ── nomarchy.hardware.* enablement — the gap ABOVE the nixos-hardware
|
||||
# commons (GuC/HuC, amd-pstate, the AMD VA-API env, GPU-compute runtimes,
|
||||
# fprintd, the NPU driver). Emitted as NOMARCHY lines the installer turns
|
||||
# into nomarchy.hardware.* in system.nix; safe bits active, heavy opt-ins
|
||||
# commented.
|
||||
if [[ "$cpu_vendor" == "GenuineIntel" || $intelgpu -eq 1 ]]; then
|
||||
echo "NOMARCHY hardware.intel.enable=true"
|
||||
echo "DETAIL nomarchy.hardware.intel: GuC/HuC on; GPU-compute runtime opt-in"
|
||||
# i915.enable_guc applies to the i915 driver only. The newer `xe` driver
|
||||
# (Lunar Lake / Battlemage / Panther Lake, recent Xe GPUs) enables GuC by
|
||||
# default and ignores the param — so turn the toggle off when xe is bound.
|
||||
if lspci -k 2>/dev/null | grep -qiE 'driver in use: xe\b'; then
|
||||
echo "NOMARCHY hardware.intel.guc=false"
|
||||
echo "DETAIL Intel GPU on the xe driver → GuC default-on (i915 param skipped)"
|
||||
fi
|
||||
fi
|
||||
if [[ "$cpu_vendor" == "AuthenticAMD" || $amdgpu -eq 1 ]]; then
|
||||
echo "NOMARCHY hardware.amd.enable=true"
|
||||
echo "DETAIL nomarchy.hardware.amd: amd-pstate + VA-API on; ROCm opt-in"
|
||||
fi
|
||||
|
||||
# Fingerprint reader — libfprint's common USB vendor IDs (Goodix,
|
||||
# Synaptics/Validity, Elan, EgisTec, Upek, AuthenTec, FocalTech, NB).
|
||||
local have_fp=0
|
||||
if command -v lsusb >/dev/null 2>&1; then
|
||||
local vid
|
||||
for vid in 27c6 06cb 138a 04f3 1c7a 147e 08ff 2808 1fae; do
|
||||
lsusb 2>/dev/null | grep -qiE "ID ${vid}:" && { have_fp=1; break; }
|
||||
done
|
||||
else
|
||||
local f
|
||||
for f in /sys/bus/usb/devices/*/idVendor; do
|
||||
[[ -e "$f" ]] || continue
|
||||
case "$(cat "$f" 2>/dev/null)" in
|
||||
27c6|06cb|138a|04f3|1c7a|147e|08ff|2808|1fae) have_fp=1; break ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
if [[ $have_fp -eq 1 ]]; then
|
||||
echo "NOMARCHY hardware.fingerprint.enable=true"
|
||||
echo "DETAIL fingerprint reader detected → fprintd (PAM login/sudo opt-in)"
|
||||
fi
|
||||
|
||||
# NPU (detect-only → a commented, experimental opt-in). Match the PCI
|
||||
# "Processing accelerators" class [1200] (or accelerator keywords) and
|
||||
# attribute by vendor — future-proof vs a per-device-ID list, so new gens
|
||||
# (e.g. Panther Lake) are caught without a code change. Known IDs kept for
|
||||
# reference: Intel VPU 8086:{7d1d,643e,ad1d,b03e}, AMD XDNA 1022:1502.
|
||||
if command -v lspci >/dev/null 2>&1; then
|
||||
local npu_line
|
||||
npu_line=$(lspci -nn 2>/dev/null \
|
||||
| grep -iE '\[1200\]|processing accelerat|neural|\[8086:(7d1d|643e|ad1d|b03e)\]|\[1022:1502\]' \
|
||||
| head -1)
|
||||
case "$npu_line" in
|
||||
*"[8086:"*|*Intel*)
|
||||
echo "NOMARCHY-NPU intel"; echo "DETAIL Intel NPU detected (opt-in, experimental)" ;;
|
||||
*"[1022:"*|*"Advanced Micro Devices"*|*AMD*)
|
||||
echo "NOMARCHY-NPU amd"; echo "DETAIL AMD XDNA NPU detected (opt-in, experimental)" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Chassis (glob test, not compgen — nixpkgs' non-interactive bash
|
||||
# is built without the completion builtins)
|
||||
local bats=(/sys/class/power_supply/BAT*)
|
||||
|
||||
@@ -210,6 +210,8 @@ section "Hardware detection"
|
||||
source "$SHARE/hardware-db.sh"
|
||||
|
||||
HW_PROFILES=()
|
||||
HW_NOMARCHY=() # NOMARCHY hardware.* assignments from detection
|
||||
NPU_VENDOR="" # "intel" | "amd" if an NPU was detected (commented opt-in)
|
||||
hw_mode="${NOMARCHY_HW:-auto}"
|
||||
if [[ "$hw_mode" == "none" ]]; then
|
||||
info "Hardware profiles skipped."
|
||||
@@ -221,6 +223,8 @@ else
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
MODULE\ *) HW_PROFILES+=("${line#MODULE }") ;;
|
||||
NOMARCHY-NPU\ *) NPU_VENDOR="${line#NOMARCHY-NPU }" ;;
|
||||
NOMARCHY\ *) HW_NOMARCHY+=("${line#NOMARCHY }") ;;
|
||||
DETAIL\ *) info "→ ${line#DETAIL }" ;;
|
||||
esac
|
||||
done <<< "$detection"
|
||||
@@ -428,6 +432,57 @@ NIX
|
||||
)
|
||||
fi
|
||||
|
||||
# Hardware enablement (nomarchy.hardware.*): what hardware-db.sh detected
|
||||
# above the nixos-hardware commons. Safe defaults active; the heavier or
|
||||
# experimental opt-ins written commented for the user to flip on.
|
||||
HARDWARE_CONFIG=""
|
||||
if [[ ${#HW_NOMARCHY[@]} -gt 0 || -n "$NPU_VENDOR" ]]; then
|
||||
has_intel=0; has_amd=0; has_fp=0; intel_guc_off=0
|
||||
if [[ ${#HW_NOMARCHY[@]} -gt 0 ]]; then
|
||||
for nm in "${HW_NOMARCHY[@]}"; do
|
||||
case "$nm" in
|
||||
hardware.intel.enable=true) has_intel=1 ;;
|
||||
hardware.intel.guc=false) intel_guc_off=1 ;;
|
||||
hardware.amd.enable=true) has_amd=1 ;;
|
||||
hardware.fingerprint.enable=true) has_fp=1 ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
hw_lines=" # Hardware enablement (auto-detected). Safe defaults are active;
|
||||
# the heavier opt-ins are commented — uncomment to turn them on."
|
||||
if [[ $has_intel -eq 1 ]]; then
|
||||
hw_lines+="
|
||||
nomarchy.hardware.intel.enable = true; # GuC/HuC firmware (i915.enable_guc=3)"
|
||||
if [[ $intel_guc_off -eq 1 ]]; then
|
||||
hw_lines+="
|
||||
nomarchy.hardware.intel.guc = false; # GPU on the xe driver → GuC is default-on"
|
||||
fi
|
||||
hw_lines+="
|
||||
# nomarchy.hardware.intel.computeRuntime = true; # OpenCL/oneVPL GPU compute (opt-in)"
|
||||
fi
|
||||
if [[ $has_amd -eq 1 ]]; then
|
||||
hw_lines+="
|
||||
nomarchy.hardware.amd.enable = true; # amd-pstate EPP + radeonsi VA-API
|
||||
# nomarchy.hardware.amd.rocm.enable = true; # ROCm GPU compute (multi-GB, opt-in)
|
||||
# nomarchy.hardware.amd.rocm.gfxOverride = \"\"; # e.g. \"11.0.0\" for an unlisted iGPU"
|
||||
fi
|
||||
if [[ $has_fp -eq 1 ]]; then
|
||||
hw_lines+="
|
||||
nomarchy.hardware.fingerprint.enable = true; # fprintd (enroll: fprintd-enroll)
|
||||
# nomarchy.hardware.fingerprint.pam = true; # use it for login + sudo (opt-in)"
|
||||
fi
|
||||
if [[ -n "$NPU_VENDOR" ]]; then
|
||||
hw_lines+="
|
||||
# nomarchy.hardware.npu.enable = true; # $NPU_VENDOR NPU driver (experimental; userspace runtime BYO)
|
||||
# nomarchy.hardware.latestKernel = true; # newest kernel if the NPU driver isn't in the shipped one"
|
||||
fi
|
||||
HARDWARE_CONFIG=$(cat <<NIX
|
||||
|
||||
$hw_lines
|
||||
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
|
||||
@@ -459,7 +514,7 @@ cat > "$FLAKE_DIR/system.nix" <<EOF
|
||||
extraGroups = [ "wheel" "networkmanager" "video" "input" ];
|
||||
initialHashedPassword = "$HASHED_PASSWORD";
|
||||
};
|
||||
$AUTOLOGIN_CONFIG$POWER_CONFIG$RESUME_CONFIG
|
||||
$AUTOLOGIN_CONFIG$POWER_CONFIG$HARDWARE_CONFIG$RESUME_CONFIG
|
||||
# Hourly/daily BTRFS timeline snapshots + nixos-rebuild-snap.
|
||||
nomarchy.system.snapper.enable = true;
|
||||
|
||||
|
||||
@@ -171,6 +171,11 @@ def run_switch() -> None:
|
||||
notify("Theme rebuild FAILED — see terminal / journal", urgency="critical")
|
||||
die("rebuild failed (state file already updated; fix and re-run)")
|
||||
notify("Theme applied ✓")
|
||||
# Waybar runs from Hyprland's exec-once (not a systemd unit HM would restart
|
||||
# on switch), so nudge the running bar to re-read its freshly rebuilt
|
||||
# config/style. SIGUSR2 = reload; harmless no-op if waybar isn't running.
|
||||
if shutil.which("pkill"):
|
||||
subprocess.run(["pkill", "--signal", "SIGUSR2", "-x", "waybar"], check=False)
|
||||
|
||||
|
||||
# ─── Theme assets (wallpapers) ────────────────────────────────────────────
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
# sunrise = "07:00";
|
||||
# };
|
||||
#
|
||||
# nomarchy.updates = { # passive update-awareness indicator + notification
|
||||
# enable = true;
|
||||
# interval = "daily"; # how often to check (systemd OnCalendar)
|
||||
# flatpak = true; # also count Flatpak updates when flatpak is on
|
||||
# };
|
||||
#
|
||||
# nomarchy.monitors = [ # declarative per-output layout, hotplug-applied
|
||||
# { name = "eDP-1"; position = "0x0"; }
|
||||
# { name = "HDMI-A-1"; position = "auto-right"; }
|
||||
|
||||
@@ -31,6 +31,18 @@
|
||||
# thermal.enable = true; # thermald (Intel CPUs)
|
||||
# };
|
||||
#
|
||||
# nomarchy.hardware = { # enablement above nixos-hardware (installer-detected)
|
||||
# intel.enable = true; # GuC/HuC firmware; installer sets this on Intel
|
||||
# intel.computeRuntime = true; # OpenCL/oneVPL GPU compute (opt-in)
|
||||
# amd.enable = true; # amd-pstate + radeonsi VA-API; installer-set on AMD
|
||||
# amd.rocm.enable = true; # ROCm GPU compute (multi-GB, opt-in)
|
||||
# amd.rocm.gfxOverride = "11.0.0"; # HSA override for an unlisted iGPU
|
||||
# fingerprint.enable = true; # fprintd; installer-set when a reader is detected
|
||||
# fingerprint.pam = true; # use the fingerprint for login + sudo
|
||||
# npu.enable = true; # on-die NPU driver (experimental; userspace runtime BYO)
|
||||
# latestKernel = true; # newest kernel for very-new hardware (drivers not yet in the default)
|
||||
# };
|
||||
#
|
||||
# nomarchy.services.tailscale.enable = true; # mesh VPN — then `sudo tailscale up`
|
||||
# nomarchy.services.syncthing.enable = true; # file sync — GUI at http://127.0.0.1:8384
|
||||
# nomarchy.services.podman.enable = true; # rootless containers (docker → podman)
|
||||
|
||||
BIN
themes/catppuccin-latte/preview.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
themes/catppuccin/preview.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
themes/ethereal/preview.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
themes/everforest/preview.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
themes/flexoki-light/preview.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
themes/gruvbox/preview.png
Normal file
|
After Width: | Height: | Size: 221 KiB |
BIN
themes/hackerman/preview.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
themes/kanagawa/preview.png
Normal file
|
After Width: | Height: | Size: 229 KiB |
BIN
themes/lumon/preview.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
themes/matte-black/preview.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
themes/miasma/preview.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
themes/nord/preview.png
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
themes/osaka-jade/preview.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
themes/retro-82/preview.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
themes/ristretto/preview.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
themes/rose-pine/preview.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
themes/summer-day/preview.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
@@ -47,8 +47,9 @@ window#waybar {
|
||||
#clock-date,
|
||||
#workspaces,
|
||||
#pulseaudio,
|
||||
#network,
|
||||
#custom-powerprofile,
|
||||
#custom-nightlight,
|
||||
#custom-updates,
|
||||
#battery,
|
||||
#tray,
|
||||
#custom-notification,
|
||||
@@ -71,6 +72,11 @@ window#waybar {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
/* The power-profile speedometer glyph renders small in its em box. */
|
||||
#custom-powerprofile {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#workspaces button.active {
|
||||
background-color: @blue;
|
||||
color: @bg0;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
"modules-left": ["custom/launcher", "clock", "clock#date"],
|
||||
"modules-center": ["hyprland/workspaces"],
|
||||
"modules-right": ["pulseaudio", "network", "custom/powerprofile", "battery", "tray", "custom/notification", "custom/powermenu"],
|
||||
"modules-right": ["pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/notification", "custom/powermenu"],
|
||||
|
||||
"hyprland/workspaces": {
|
||||
"disable-scroll": true,
|
||||
@@ -46,14 +46,6 @@
|
||||
"tooltip-format": "Playing at {volume}%"
|
||||
},
|
||||
|
||||
"network": {
|
||||
"format-wifi": " {signalStrength}%",
|
||||
"format-ethernet": "",
|
||||
"format-disconnected": "",
|
||||
"on-click": "sh -c '$TERMINAL -e nmtui'",
|
||||
"tooltip-format": "{ipaddr} via {gwaddr}"
|
||||
},
|
||||
|
||||
"battery": {
|
||||
"interval": 60,
|
||||
"states": { "warning": 30, "critical": 15 },
|
||||
@@ -87,6 +79,21 @@
|
||||
"on-click": "nomarchy-powerprofile-cycle"
|
||||
},
|
||||
|
||||
"custom/nightlight": {
|
||||
"return-type": "json",
|
||||
"interval": 3,
|
||||
"exec": "nomarchy-nightlight status",
|
||||
"on-click": "nomarchy-nightlight toggle"
|
||||
},
|
||||
|
||||
"custom/updates": {
|
||||
"return-type": "json",
|
||||
"interval": 1800,
|
||||
"signal": 9,
|
||||
"exec": "nomarchy-updates status",
|
||||
"on-click": "sh -c '$TERMINAL -e nomarchy-updates upgrade'"
|
||||
},
|
||||
|
||||
"custom/notification": {
|
||||
"format": "{icon}",
|
||||
"return-type": "json",
|
||||
|
||||
BIN
themes/summer-night/preview.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
@@ -53,8 +53,9 @@ window#waybar {
|
||||
#clock.date,
|
||||
#workspaces,
|
||||
#pulseaudio,
|
||||
#network,
|
||||
#custom-powerprofile,
|
||||
#custom-nightlight,
|
||||
#custom-updates,
|
||||
#idle_inhibitor,
|
||||
#battery,
|
||||
#custom-notification,
|
||||
@@ -75,6 +76,11 @@ window#waybar {
|
||||
color: @fg;
|
||||
}
|
||||
|
||||
/* The power-profile speedometer glyph renders small in its em box. */
|
||||
#custom-powerprofile {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#tray > .needs-attention {
|
||||
color: @green;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
|
||||
"modules-center": ["hyprland/workspaces"],
|
||||
"modules-right": ["idle_inhibitor", "pulseaudio", "network", "custom/powerprofile", "battery", "tray", "custom/notification", "custom/powermenu"],
|
||||
"modules-right": ["idle_inhibitor", "pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/notification", "custom/powermenu"],
|
||||
|
||||
"hyprland/workspaces": {
|
||||
"disable-scroll": true,
|
||||
@@ -55,14 +55,6 @@
|
||||
"tooltip-format": "{desc} | {volume}%"
|
||||
},
|
||||
|
||||
"network": {
|
||||
"format-wifi": " {essid}",
|
||||
"format-ethernet": "",
|
||||
"format-disconnected": "",
|
||||
"on-click": "sh -c '$TERMINAL -e nmtui'",
|
||||
"tooltip-format": "{ipaddr} via {gwaddr}"
|
||||
},
|
||||
|
||||
"battery": {
|
||||
"interval": 30,
|
||||
"states": { "warning": 25, "critical": 10 },
|
||||
@@ -94,6 +86,21 @@
|
||||
"on-click": "nomarchy-powerprofile-cycle"
|
||||
},
|
||||
|
||||
"custom/nightlight": {
|
||||
"return-type": "json",
|
||||
"interval": 3,
|
||||
"exec": "nomarchy-nightlight status",
|
||||
"on-click": "nomarchy-nightlight toggle"
|
||||
},
|
||||
|
||||
"custom/updates": {
|
||||
"return-type": "json",
|
||||
"interval": 1800,
|
||||
"signal": 9,
|
||||
"exec": "nomarchy-updates status",
|
||||
"on-click": "sh -c '$TERMINAL -e nomarchy-updates upgrade'"
|
||||
},
|
||||
|
||||
"custom/notification": {
|
||||
"format": "{icon}",
|
||||
"return-type": "json",
|
||||
|
||||
BIN
themes/tokyo-night/preview.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
themes/vantablack/preview.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
themes/white/preview.png
Normal file
|
After Width: | Height: | Size: 49 KiB |