From c0fef4fa5796a707a64ad303507a949d5804c102 Mon Sep 17 00:00:00 2001 From: Matan Bendix Shenhav Date: Sat, 11 Oct 2025 18:01:01 +0200 Subject: [PATCH] refactor(flake): split the flake This extracts the package, home module and nixos systemd service from the flake into distinct Nix modules. Additionally, the package is updated to use default the qt wrapper rather than the custom one previously used; this conforms to current best practices in packaging Qt apps. Several improvements are made to the Nix style as well. --- flake.nix | 310 +++++-------------------------------------- nix/home-module.nix | 114 ++++++++++++++++ nix/nixos-module.nix | 56 ++++++++ nix/package.nix | 108 +++++++++++++++ 4 files changed, 313 insertions(+), 275 deletions(-) create mode 100644 nix/home-module.nix create mode 100644 nix/nixos-module.nix create mode 100644 nix/package.nix diff --git a/flake.nix b/flake.nix index 31730a61..54381f04 100644 --- a/flake.nix +++ b/flake.nix @@ -11,287 +11,47 @@ }; }; - outputs = - { - self, - nixpkgs, - systems, - quickshell, - ... - }: - let - eachSystem = nixpkgs.lib.genAttrs (import systems); - in - { - formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra); + outputs = { + self, + nixpkgs, + systems, + quickshell, + ... + }: let + eachSystem = nixpkgs.lib.genAttrs (import systems); + in { + formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra); - packages = eachSystem ( - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - qs = quickshell.packages.${system}.default.override { + packages = eachSystem ( + system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + default = pkgs.callPackage ./nix/package.nix { + version = self.rev or self.dirtyRev or "dirty"; + quickshell = quickshell.packages.${system}.default.override { withX11 = false; withI3 = true; }; - - runtimeDeps = - with pkgs; - [ - bash - bluez - brightnessctl - cava - cliphist - coreutils - ddcutil - file - findutils - libnotify - matugen - networkmanager - wlsunset - wl-clipboard - ] - ++ lib.optionals (pkgs.stdenv.hostPlatform.isx86_64) [ gpu-screen-recorder ]; - - fontconfig = pkgs.makeFontsConf { - fontDirectories = [ - pkgs.roboto - pkgs.inter-nerdfont - ]; - }; - in - { - default = pkgs.stdenv.mkDerivation { - pname = "noctalia-shell"; - version = self.rev or self.dirtyRev or "dirty"; - src = ./.; - - nativeBuildInputs = [ - pkgs.gcc - pkgs.makeWrapper - pkgs.qt6.wrapQtAppsHook - ]; - buildInputs = [ - qs - pkgs.xkeyboard_config - pkgs.qt6.qtbase - ]; - propagatedBuildInputs = runtimeDeps; - - installPhase = '' - mkdir -p $out/share/noctalia-shell - cp -r ./* $out/share/noctalia-shell - - makeWrapper ${qs}/bin/qs $out/bin/noctalia-shell \ - --prefix PATH : "${pkgs.lib.makeBinPath runtimeDeps}" \ - --set FONTCONFIG_FILE "${fontconfig}" \ - --add-flags "-p $out/share/noctalia-shell" - ''; - - meta = { - description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell."; - homepage = "https://github.com/noctalia-dev/noctalia-shell"; - license = pkgs.lib.licenses.mit; - mainProgram = "noctalia-shell"; - }; - }; - } - ); - - defaultPackage = eachSystem (system: self.packages.${system}.default); - - homeModules.default = - { - config, - lib, - pkgs, - ... - }: - let - cfg = config.programs.noctalia-shell; - defaultSettings = builtins.fromJSON (builtins.readFile ./Assets/settings-default.json); - - # Deep merge user settings with defaults - mergedSettings = - if cfg.settings == null then - defaultSettings - else if builtins.isAttrs cfg.settings then - lib.recursiveUpdate defaultSettings cfg.settings - else - cfg.settings; # Pass through strings/paths as-is - in - { - options.programs.noctalia-shell = { - enable = lib.mkEnableOption "Noctalia shell configuration"; - - settings = lib.mkOption { - type = - with lib.types; - nullOr (oneOf [ - attrs - str - path - ]); - default = null; - example = lib.literalExpression '' - { - bar = { - position = "bottom"; - floating = true; - backgroundOpacity = 0.95; - }; - general = { - animationSpeed = 1.5; - radiusRatio = 1.2; - }; - colorSchemes = { - darkMode = true; - useWallpaperColors = true; - }; - } - ''; - description = '' - Noctalia shell configuration settings as an attribute set, string - or filepath, to be written to ~/.config/noctalia/settings.json. - When provided as an attribute set, it will be deep-merged with - the default settings. - ''; - }; - - colors = lib.mkOption { - type = - with lib.types; - nullOr (oneOf [ - attrs - str - path - ]); - default = null; - example = lib.literalExpression '' - { - mError = "#dddddd"; - mOnError = "#111111"; - mOnPrimary = "#111111"; - mOnSecondary = "#111111"; - mOnSurface = "#828282"; - mOnSurfaceVariant = "#5d5d5d"; - mOnTertiary = "#111111"; - mOutline = "#3c3c3c"; - mPrimary = "#aaaaaa"; - mSecondary = "#a7a7a7"; - mShadow = "#000000"; - mSurface = "#111111"; - mSurfaceVariant = "#191919"; - mTertiary = "#cccccc"; - } - ''; - description = '' - Noctalia shell color configuration as an attribute set, string - or filepath, to be written to ~/.config/noctalia/colors.json. - ''; - }; - }; - - config = - let - restart = '' - ${pkgs.systemd}/bin/systemctl --user try-restart noctalia-shell.service 2>/dev/null || true - ''; - useApp2Unit = mergedSettings.appLauncher.useApp2Unit or false; - in - lib.mkIf cfg.enable { - home.packages = lib.optional useApp2Unit pkgs.app2unit; - - xdg.configFile = { - "noctalia/settings.json" = { - onChange = restart; - } - // ( - if builtins.isAttrs mergedSettings then - { text = builtins.toJSON mergedSettings + "\n"; } - else if builtins.isString mergedSettings then - { text = mergedSettings; } - else - { source = mergedSettings; } - ); - "noctalia/colors.json" = lib.mkIf (cfg.colors != null) ( - { - onChange = restart; - } - // ( - if builtins.isAttrs cfg.colors then - { text = builtins.toJSON cfg.colors; } - else if builtins.isString cfg.colors then - { text = cfg.colors; } - else - { source = cfg.colors; } - ) - ); - }; - }; }; + } + ); - nixosModules.default = - { - config, - lib, - pkgs, - ... - }: - let - cfg = config.services.noctalia-shell; - in - { - options.services.noctalia-shell = { - enable = lib.mkEnableOption "Noctalia shell systemd service"; + defaultPackage = eachSystem (system: self.packages.${system}.default); - package = lib.mkOption { - type = lib.types.package; - default = self.packages.${pkgs.system}.default; - description = "The noctalia-shell package to use"; - }; - - target = lib.mkOption { - type = lib.types.str; - default = "graphical-session.target"; - example = "hyprland-session.target"; - description = "The systemd target for the noctalia-shell service."; - }; - }; - - config = lib.mkIf cfg.enable { - systemd.user.services.noctalia-shell = { - description = "Noctalia Shell - Wayland desktop shell"; - documentation = [ "https://github.com/noctalia-dev/noctalia-shell" ]; - after = [ cfg.target ]; - partOf = [ cfg.target ]; - wantedBy = [ cfg.target ]; - restartTriggers = [ cfg.package ]; - - environment = { - PATH = lib.mkForce null; - }; - - unitConfig = { - StartLimitIntervalSec = 60; - StartLimitBurst = 3; - }; - - serviceConfig = { - ExecStart = "${cfg.package}/bin/noctalia-shell"; - Restart = "on-failure"; - RestartSec = 3; - TimeoutStartSec = 10; - TimeoutStopSec = 5; - Environment = [ - "NOCTALIA_SETTINGS_FALLBACK=%h/.config/noctalia/gui-settings.json" - ]; - }; - }; - - environment.systemPackages = [ cfg.package ]; - }; - }; + homeModules.default = { + pkgs, + lib, + ... + }: { + imports = [./nix/home-module.nix]; + programs.noctalia-shell.app2unit.package = + lib.mkDefault + nixpkgs.legacyPackages.${pkgs.system}.app2unit; }; + + nixosModules.default = {pkgs, ...}: { + imports = [./nix/nixos-module.nix]; + services.noctalia-shell.package = self.packages.${pkgs.system}.default; + }; + }; } diff --git a/nix/home-module.nix b/nix/home-module.nix new file mode 100644 index 00000000..08410911 --- /dev/null +++ b/nix/home-module.nix @@ -0,0 +1,114 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.programs.noctalia-shell; + defaultSettings = builtins.fromJSON (builtins.readFile ../Assets/settings-default.json); + extractAttrs = x: + if builtins.isAttrs x + then x + else if builtins.isString x + then builtins.fromJson x + else builtins.fromJson (builtins.readFile x); +in { + options.programs.noctalia-shell = { + enable = lib.mkEnableOption "Noctalia shell configuration"; + + settings = lib.mkOption { + type = with lib.types; + nullOr (oneOf [ + attrs + str + path + ]); + default = {}; + apply = x: lib.recursiveUpdate defaultSettings (extractAttrs x); + example = lib.literalExpression '' + { + bar = { + position = "bottom"; + floating = true; + backgroundOpacity = 0.95; + }; + general = { + animationSpeed = 1.5; + radiusRatio = 1.2; + }; + colorSchemes = { + darkMode = true; + useWallpaperColors = true; + }; + } + ''; + description = '' + Noctalia shell configuration settings as an attribute set, string + or filepath, to be written to ~/.config/noctalia/settings.json. + When provided as an attribute set, it will be deep-merged with + the default settings. + ''; + }; + + colors = lib.mkOption { + type = with lib.types; + nullOr (oneOf [ + attrs + str + path + ]); + default = {}; + apply = extractAttrs; + example = lib.literalExpression '' + { + mError = "#dddddd"; + mOnError = "#111111"; + mOnPrimary = "#111111"; + mOnSecondary = "#111111"; + mOnSurface = "#828282"; + mOnSurfaceVariant = "#5d5d5d"; + mOnTertiary = "#111111"; + mOutline = "#3c3c3c"; + mPrimary = "#aaaaaa"; + mSecondary = "#a7a7a7"; + mShadow = "#000000"; + mSurface = "#111111"; + mSurfaceVariant = "#191919"; + mTertiary = "#cccccc"; + } + ''; + description = '' + Noctalia shell color configuration as an attribute set, string + or filepath, to be written to ~/.config/noctalia/colors.json. + ''; + }; + + app2unit.package = lib.mkOption { + type = lib.types.package; + description = '' + The app2unit package to use when appLauncher.useApp2Unit is enabled. + ''; + }; + }; + + config = let + restart = '' + ${pkgs.systemd}/bin/systemctl --user try-restart noctalia-shell.service 2>/dev/null || true + ''; + useApp2Unit = cfg.settings.appLauncher.useApp2Unit or false; + in + lib.mkIf cfg.enable { + home.packages = lib.optional useApp2Unit cfg.app2unit.package; + + xdg.configFile = { + "noctalia/settings.json" = { + onChange = restart; + text = builtins.toJSON cfg.settings; + }; + "noctalia/colors.json" = lib.mkIf (cfg.colors != {}) { + onChange = restart; + text = builtins.toJSON cfg.colors; + }; + }; + }; +} diff --git a/nix/nixos-module.nix b/nix/nixos-module.nix new file mode 100644 index 00000000..bbab247c --- /dev/null +++ b/nix/nixos-module.nix @@ -0,0 +1,56 @@ +{ + config, + lib, + ... +}: let + cfg = config.services.noctalia-shell; +in { + options.services.noctalia-shell = { + enable = lib.mkEnableOption "Noctalia shell systemd service"; + + package = lib.mkOption { + type = lib.types.package; + description = "The noctalia-shell package to use"; + }; + + target = lib.mkOption { + type = lib.types.str; + default = "graphical-session.target"; + example = "hyprland-session.target"; + description = "The systemd target for the noctalia-shell service."; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.user.services.noctalia-shell = { + description = "Noctalia Shell - Wayland desktop shell"; + documentation = ["https://github.com/noctalia-dev/noctalia-shell"]; + after = [cfg.target]; + partOf = [cfg.target]; + wantedBy = [cfg.target]; + restartTriggers = [cfg.package]; + + environment = { + PATH = lib.mkForce null; + }; + + unitConfig = { + StartLimitIntervalSec = 60; + StartLimitBurst = 3; + }; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/noctalia-shell"; + Restart = "on-failure"; + RestartSec = 3; + TimeoutStartSec = 10; + TimeoutStopSec = 5; + Environment = [ + "NOCTALIA_SETTINGS_FALLBACK=%h/.config/noctalia/gui-settings.json" + ]; + }; + }; + + environment.systemPackages = [cfg.package]; + }; +} diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 00000000..a5d07917 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,108 @@ +{ + version ? "dirty", + lib, + stdenv, + # build + gcc, + qt6, + quickshell, + xkeyboard_config, + # runtime deps + bash, + bluez, + brightnessctl, + cava, + cliphist, + coreutils, + ddcutil, + file, + findutils, + libnotify, + matugen, + networkmanager, + wlsunset, + wl-clipboard, + gpu-screen-recorder, # optional + # fonts + makeFontsConf, + roboto, + inter-nerdfont, +}: let + src = lib.cleanSourceWith { + src = ../.; + filter = path: type: + !(builtins.any (prefix: lib.path.hasPrefix (../. + prefix) (/. + path)) [ + /.github + /Assets/Screenshots + /Assets/Wallpaper + /Bin/dev + /nix + /LICENSE + /README.md + /flake.nix + /flake.lock + ]); + }; + + runtimeDeps = + [ + bash + bluez + brightnessctl + cava + cliphist + coreutils + ddcutil + file + findutils + libnotify + matugen + networkmanager + wlsunset + wl-clipboard + ] + ++ lib.optionals (stdenv.hostPlatform.isx86_64) [gpu-screen-recorder]; + + fontconfig = makeFontsConf { + fontDirectories = [ + roboto + inter-nerdfont + ]; + }; +in + stdenv.mkDerivation { + pname = "noctalia-shell"; + inherit version src; + + nativeBuildInputs = [ + gcc + qt6.wrapQtAppsHook + ]; + buildInputs = [ + quickshell + xkeyboard_config + qt6.qtbase + ]; + propagatedBuildInputs = runtimeDeps; + + installPhase = '' + mkdir -p $out/share/noctalia-shell $out/bin + cp -r ./* $out/share/noctalia-shell + cp ${quickshell}/bin/qs $out/bin/noctalia-shell + ''; + + preFixup = '' + qtWrapperArgs+=( + --prefix PATH : ${lib.makeBinPath runtimeDeps} + --set FONTCONFIG_FILE ${fontconfig} + --add-flags "-p $out/share/noctalia-shell" + ) + ''; + + meta = { + description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell."; + homepage = "https://github.com/noctalia-dev/noctalia-shell"; + license = lib.licenses.mit; + mainProgram = "noctalia-shell"; + }; + }