diff --git a/README.md b/README.md index 6dbaa38..078b56b 100644 --- a/README.md +++ b/README.md @@ -230,8 +230,6 @@ workspaces · `Print` region screenshot. `tools/import-palettes.py themes/`. ## Roadmap -- Fix UI: Install Nerd fonts by default (10 most popular ones) and check if the theme's font is installed and correctly set across the sytem. -- Check if distro is shipping drivers (a test on real hardware was missing wifi driver) - We need to start creating a menu system (for apps launcher and for theme switching) - We need to add networkManager applet to waybar so user can easily connect to internet - **Faster switches:** move `backgrounds/` out of the flake source (the 86 MB @@ -241,3 +239,4 @@ workspaces · `Print` region screenshot. - 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 +- swaync notification center (notify-send currently renders bare) diff --git a/flake.lock b/flake.lock index 075584c..7546885 100644 --- a/flake.lock +++ b/flake.lock @@ -208,11 +208,11 @@ ] }, "locked": { - "lastModified": 1780281641, - "narHash": "sha256-M/+hUKoKbHXpV0xGVfELbN1Ds1aoe3pL5p5/t46YhVo=", + "lastModified": 1779766384, + "narHash": "sha256-P7Ohnlq8b8b2fU+Sgkrej7LBTM60LBTkHleLuYzmLmU=", "owner": "nix-community", "repo": "NUR", - "rev": "30f9ae2f04174de63ba8bcf3580ca90843b28a01", + "rev": "57800b7ab648725ccd33551d01484ee14952ad3f", "type": "github" }, "original": { @@ -249,15 +249,16 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1781018772, - "narHash": "sha256-C+cGIUaC6dqfwTbI+BwCd572PbESGA3WYxR1sLTqxkY=", - "owner": "danth", + "lastModified": 1780702455, + "narHash": "sha256-+srjPGNy67nKytYwdlepycL51IG6S34sS4MKRZXK8G0=", + "owner": "nix-community", "repo": "stylix", - "rev": "a378e4c09031fb15a4d65da88aa628f71fc52f6b", + "rev": "54fa19702f4f2c7f6a981a92850678933588af9a", "type": "github" }, "original": { - "owner": "danth", + "owner": "nix-community", + "ref": "release-26.05", "repo": "stylix", "type": "github" } diff --git a/flake.nix b/flake.nix index 11862e5..fe28559 100644 --- a/flake.nix +++ b/flake.nix @@ -10,9 +10,12 @@ }; stylix = { - url = "github:danth/stylix"; + # Track the release branch matching nixpkgs/HM — master is built + # against unstable and warns about the version skew on every + # activation. (No home-manager input to follow anymore: stylix + # vendors its HM integration.) + url = "github:nix-community/stylix/release-26.05"; inputs.nixpkgs.follows = "nixpkgs"; - inputs.home-manager.follows = "home-manager"; }; # Pinned by Nomarchy so distro + hardware quirks are tested together @@ -270,6 +273,20 @@ representativeInstall.config.system.build.toplevel template.homeConfigurations.${username}.activationPackage + # The live session's `home-manager switch` builds the + # REPO's standalone generation (the seeded ~/.nomarchy + # flake) — not the template's, and not the NixOS-module + # one baked into the ISO. It has drvs the other two + # variants don't (user-dbus-services et al.), so pin it + # and its direct build inputs too. + self.homeConfigurations.${username}.activationPackage + self.homeConfigurations.${username}.activationPackage.inputDerivation + # home-files is rebuilt per theme and COPIES some of its + # inputs (user-dbus-services) instead of referencing + # them — the activation closure doesn't carry those, so + # pin home-files' direct build inputs separately. + self.homeConfigurations.${username}.config.home-files.inputDerivation + # A real install rebuilds the drvs that embed the # user's hostname/username/UUIDs (etc, initrd, units, # toplevel, HM activation). inputDerivation pins each @@ -278,6 +295,15 @@ representativeInstall.config.system.build.etc.inputDerivation representativeInstall.config.system.build.initialRamdisk.inputDerivation template.homeConfigurations.${username}.activationPackage.inputDerivation + ] ++ [ + # A theme switch re-renders stylix's base16 templates + # (gtk.css, kvantum, …) on the machine: the renderer and + # the stdenv its trivial drvs build with must be on + # board, or an offline `apply ` cascades into + # building mustache-go (and eventually stdenv) from + # source. Found with tools/vm/gap-analysis.py. + pkgs.mustache-go + pkgs.stdenv ] ++ [ # …and the second-level build tools those rebuilds need # (their parents are themselves rebuilt, so parent diff --git a/hosts/live.nix b/hosts/live.nix index 786a0bc..5e8c04a 100644 --- a/hosts/live.nix +++ b/hosts/live.nix @@ -9,9 +9,11 @@ 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; + # Do NOT touch networking.wireless here: since NixOS 26.05 the + # NetworkManager module drives its wifi backend through it + # (wireless.enable = true + dbusControlled). Force-disabling it + # kills NM's supplicant — wifi devices vanish from nmtui even + # though the driver is loaded (seen on a Latitude 5410 / AX201). # ── Live user: no password, straight into the desktop ────────────── users.users.${username} = { @@ -24,7 +26,7 @@ # Boot straight into Hyprland once; logging out lands on tuigreet. services.greetd.settings.initial_session = { - command = "Hyprland"; + command = "start-hyprland"; user = username; }; diff --git a/modules/home/hyprland.nix b/modules/home/hyprland.nix index 6a44b8a..5584ad1 100644 --- a/modules/home/hyprland.nix +++ b/modules/home/hyprland.nix @@ -99,6 +99,13 @@ in # dispatcher still exists); setting it aborts config parsing. dwindle.preserve_split = true; + # Hyprland 0.51+ replaced gestures:workspace_swipe with the + # gesture keyword — without this, touchpads have NO gestures. + gesture = [ + "3, horizontal, workspace" + "4, pinch, fullscreen" + ]; + misc = { disable_hyprland_logo = true; disable_splash_rendering = true; @@ -129,14 +136,25 @@ in ", Print, exec, grim -g \"$(slurp)\" - | wl-copy" ] ++ workspaceBinds; - binde = [ - ", XF86AudioRaiseVolume, exec, pamixer -i 5" - ", XF86AudioLowerVolume, exec, pamixer -d 5" - ", XF86AudioMute, exec, pamixer -t" + # Media keys: wpctl talks to wireplumber directly (no + # pipewire-pulse shim like pamixer); e = repeat, l = also on a + # locked screen — the standard flags for hardware keys. + bindel = [ + ", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+" + ", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-" ", XF86MonBrightnessUp, exec, brightnessctl set +5%" ", XF86MonBrightnessDown, exec, brightnessctl set 5%-" ]; + bindl = [ + ", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" + ", XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle" + ", XF86AudioPlay, exec, playerctl play-pause" + ", XF86AudioPause, exec, playerctl play-pause" + ", XF86AudioNext, exec, playerctl next" + ", XF86AudioPrev, exec, playerctl previous" + ]; + bindm = [ "$mod, mouse:272, movewindow" "$mod, mouse:273, resizewindow" diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index f4e03f7..431a0ef 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -28,12 +28,14 @@ in enable = lib.mkDefault true; settings = { default_session = { - command = lib.mkDefault "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd Hyprland"; + # start-hyprland is Hyprland 0.55's watchdog launcher; running + # the bare binary makes every session print a warning. + command = lib.mkDefault "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd start-hyprland"; user = "greeter"; }; # Boot straight into the session once; logout → normal greeter. initial_session = lib.mkIf (cfg.greeter.autoLogin != null) { - command = "Hyprland"; + command = "start-hyprland"; user = cfg.greeter.autoLogin; }; }; @@ -60,6 +62,13 @@ in hardware.bluetooth.enable = lib.mkDefault cfg.bluetooth.enable; services.blueman.enable = lib.mkDefault cfg.bluetooth.enable; + # ── Firmware ───────────────────────────────────────────────────── + # Blobs for in-kernel drivers: wifi (iwlwifi/ath/rtw/brcm), SOF + # audio, Bluetooth. Drivers ship with the kernel, but without these + # a real machine can boot with no wifi — QEMU never catches it. + # nixos-generate-config also keys CPU microcode off this flag. + hardware.enableRedistributableFirmware = lib.mkDefault true; + # ── 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. @@ -79,9 +88,23 @@ in }; # ── Fonts ──────────────────────────────────────────────────────── + # The ten most popular Nerd Fonts ship by default, so any of them + # can be named in the theme state's fonts.mono and actually resolve + # (nomarchy-theme-sync warns when a configured font is missing). fonts = { packages = with pkgs; [ nerd-fonts.jetbrains-mono + nerd-fonts.fira-code + nerd-fonts.caskaydia-cove + nerd-fonts.hack + nerd-fonts.sauce-code-pro + nerd-fonts.meslo-lg + nerd-fonts.roboto-mono + nerd-fonts.ubuntu-mono + # iosevka would be next by popularity, but its package is 1.1 GB + # (every weight × variant) — too heavy for the ISO and closures. + nerd-fonts.mononoki + nerd-fonts.inconsolata inter noto-fonts noto-fonts-color-emoji diff --git a/pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py b/pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py index ba136a7..7f23141 100644 --- a/pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py +++ b/pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py @@ -109,6 +109,29 @@ def deep_merge(base: dict, override: dict) -> dict: return out +# ─── Font verification ──────────────────────────────────────────────────── + +def check_fonts(state: dict) -> None: + """Warn when a configured font family isn't installed. fontconfig + substitutes silently, so a typo'd or missing fonts.mono would just + render the whole desktop in some fallback with no error anywhere.""" + if shutil.which("fc-match") is None: + return + for key in ("mono", "ui"): + name = (state.get("fonts") or {}).get(key) + if not name: + continue + result = subprocess.run(["fc-match", "-f", "%{family}", name], + capture_output=True, text=True) + families = [f.strip().lower() for f in result.stdout.split(",")] + if result.returncode != 0 or name.strip().lower() not in families: + fallback = result.stdout.strip() or "unknown" + print(f"nomarchy-theme-sync: warning: fonts.{key} '{name}' is not " + f"installed — fontconfig will substitute '{fallback}'", + file=sys.stderr) + notify(f"Font '{name}' is not installed — using '{fallback}'") + + # ─── Rebuild dispatch ───────────────────────────────────────────────────── def run_switch() -> None: @@ -219,6 +242,7 @@ def cmd_apply(args) -> None: state = deep_merge(load_state(), preset) write_state(state) log(f"theme: {state.get('name', args.theme)}") + check_fonts(state) if not args.no_switch: run_switch() apply_wallpaper(state) @@ -241,6 +265,8 @@ def cmd_set(args) -> None: write_state(state) log(f"set {args.path} = {value!r}") + if args.path.startswith("fonts."): + check_fonts(state) if not args.no_switch: run_switch() apply_wallpaper(state)