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.
This commit is contained in:
Matan Bendix Shenhav
2025-10-11 18:01:01 +02:00
parent f36c8fe73a
commit c0fef4fa57
4 changed files with 313 additions and 275 deletions
+35 -275
View File
@@ -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;
};
};
}
+114
View File
@@ -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;
};
};
};
}
+56
View File
@@ -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];
};
}
+108
View File
@@ -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";
};
}