noctalia: init module

Noctalia is a native Wayland desktop shell for people who want a polished, configurable Linux desktop without stitching together a separate bar, launcher, notification daemon, lock screen, wallpaper tool, and settings UI.
This commit is contained in:
Rachit Kumar Verma
2026-08-24 08:33:19 +05:30
committed by GitHub
parent 9f56e690e4
commit 03f4cd46bc
9 changed files with 459 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
{ pkgs, ... }:
{
time = "2026-08-01T09:29:44+00:00";
condition = pkgs.stdenv.hostPlatform.isLinux;
message = ''
A new module is available `programs.noctalia`.
Noctalia is a sleek, customizable shell crafted for Wayland.
'';
}

View File

@@ -0,0 +1,153 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.programs.noctalia;
jsonFormat = pkgs.formats.json { };
tomlFormat = pkgs.formats.toml { };
generateConfig =
format: name: value:
if lib.isString value then
pkgs.writeText name value
else if builtins.isPath value || lib.isStorePath value then
value
else
format.generate name value;
generateToml = generateConfig tomlFormat;
generateJson = generateConfig jsonFormat;
in
{
meta.maintainers = with lib.maintainers; [
rachitvrma
];
options.programs.noctalia = {
enable = lib.mkEnableOption "noctalia, a lightweight Wayland shell and bar";
systemd.enable = lib.mkEnableOption "a systemd user service for noctalia";
package = lib.mkPackageOption pkgs "noctalia" { nullable = true; };
checkConfig = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Validate the configuration file at build time.";
};
settings = lib.mkOption {
type =
with lib.types;
oneOf [
tomlFormat.type
str
path
];
default = { };
description = ''
Default settings for noctalia, can be written as:
- A Nix attrset (converted to TOML via nixpkgs' tomlFormat)
- A raw TOML string
- A path to a `.toml` file
See <https://docs.noctalia.dev/v5> for more information and examples.
Note: these settings can still be overwritten at runtime via the settings menu.
'';
example = lib.literalExpression ''
{
shell = {
font = "JetBrainsMono Nerd Font";
settings_show_advanced = true;
};
theme = {
mode = "dark";
source = "builtin";
builtin = "Catppuccin";
};
}
'';
};
customPalettes = lib.mkOption {
type =
with lib.types;
attrsOf (oneOf [
jsonFormat.type
str
path
]);
default = { };
description = ''
Custom color palettes, keyed by name. Each value can be:
- A Nix attrset (converted to JSON)
- A raw JSON string
- A path to a {file}`.json` file
See <https://docs.noctalia.dev/v5/theming/#custom_palette>.
'';
};
};
config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "programs.noctalia" pkgs lib.platforms.linux)
{
assertion = !cfg.systemd.enable || cfg.package != null;
message = "programs.noctalia.package cannot be null when programs.noctalia.systemd.enable is true";
}
];
systemd.user.services.noctalia = lib.mkIf cfg.systemd.enable {
Unit = {
Description = "Noctalia - A lightweight Wayland shell and bar";
Documentation = "https://docs.noctalia.dev/v5/";
PartOf = [ config.wayland.systemd.target ];
After = [ config.wayland.systemd.target ];
X-Restart-Triggers =
lib.optional (cfg.settings != { }) "${config.xdg.configFile."noctalia/config.toml".source}"
++ lib.mapAttrsToList (
name: _: "${config.xdg.configFile."noctalia/palettes/${name}.json".source}"
) cfg.customPalettes;
};
Service = {
ExecStart = lib.getExe cfg.package;
Restart = "on-failure";
};
Install.WantedBy = [ config.wayland.systemd.target ];
};
home.packages = lib.optional (cfg.package != null) cfg.package;
xdg = {
configFile = lib.mkMerge [
(lib.mkIf (cfg.settings != { }) {
"noctalia/config.toml".source =
let
rawConfig = generateToml "config.toml" cfg.settings;
in
if cfg.checkConfig && cfg.package != null then
pkgs.runCommand "noctalia-config.toml" { } ''
${lib.getExe cfg.package} config validate ${rawConfig}
cp ${rawConfig} $out
''
else
rawConfig;
})
(lib.mapAttrs' (
name: palette:
lib.nameValuePair "noctalia/palettes/${name}.json" {
source = generateJson "${name}-palette.json" palette;
}
) cfg.customPalettes)
];
};
};
}

View File

@@ -0,0 +1,106 @@
{
programs.noctalia = {
enable = true;
checkConfig = false;
customPalettes.example_gruvbox = {
dark = {
mError = "#ea6962";
mHover = "#89b482";
mOnError = "#141617";
mOnHover = "#141617";
mOnPrimary = "#141617";
mOnSecondary = "#141617";
mOnSurface = "#ddc7a1";
mOnSurfaceVariant = "#bdae93";
mOnTertiary = "#141617";
mOutline = "#5a524c";
mPrimary = "#d3869b";
mSecondary = "#7daea3";
mShadow = "#141617";
mSurface = "#141617";
mSurfaceVariant = "#1d2021";
mTertiary = "#89b482";
terminal = {
background = "#141617";
bright = {
black = "#5a524c";
blue = "#7daea3";
cyan = "#89b482";
green = "#a9b665";
magenta = "#d3869b";
red = "#ea6962";
white = "#ebdbb2";
yellow = "#d8a657";
};
cursor = "#ddc7a1";
cursorText = "#141617";
foreground = "#ddc7a1";
normal = {
black = "#1d2021";
blue = "#7daea3";
cyan = "#89b482";
green = "#a9b665";
magenta = "#d3869b";
red = "#ea6962";
white = "#ddc7a1";
yellow = "#d8a657";
};
selectionBg = "#5a524c";
selectionFg = "#ddc7a1";
};
};
light = {
mError = "#cc241d";
mHover = "#427b58";
mOnError = "#fbf1c7";
mOnHover = "#fbf1c7";
mOnPrimary = "#fbf1c7";
mOnSecondary = "#fbf1c7";
mOnSurface = "#3c3836";
mOnSurfaceVariant = "#7c6f64";
mOnTertiary = "#fbf1c7";
mOutline = "#bdae93";
mPrimary = "#b16286";
mSecondary = "#458588";
mShadow = "#d5c4a1";
mSurface = "#fbf1c7";
mSurfaceVariant = "#f2e5bc";
mTertiary = "#427b58";
terminal = {
background = "#fbf1c7";
bright = {
black = "#928374";
blue = "#076678";
cyan = "#427b58";
green = "#79740e";
magenta = "#8f3f71";
red = "#9d0006";
white = "#3c3836";
yellow = "#b57614";
};
cursor = "#3c3836";
cursorText = "#fbf1c7";
foreground = "#3c3836";
normal = {
black = "#fbf1c7";
blue = "#458588";
cyan = "#689d6a";
green = "#98971a";
magenta = "#b16286";
red = "#cc241d";
white = "#7c6f64";
yellow = "#d79921";
};
selectionBg = "#3c3836";
selectionFg = "#fbf1c7";
};
};
};
};
nmt.script = ''
local customPalette="home-files/.config/noctalia/palettes/example_gruvbox.json"
assertFileExists $customPalette
assertFileContent $customPalette "${./expected-custom-palette.json}"
'';
}

View File

@@ -0,0 +1,7 @@
{ lib, pkgs, ... }:
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
noctalia-empty-config = ./empty-config.nix;
noctalia-example-config = ./example-config.nix;
noctalia-custom-palette = ./custom-palette.nix;
noctalia-systemd-enabled = ./systemd-enabled.nix;
}

View File

@@ -0,0 +1,7 @@
{
programs.noctalia.enable = true;
nmt.script = ''
assertPathNotExists "home-files/.config/noctalia";
'';
}

View File

@@ -0,0 +1,36 @@
{
programs.noctalia = {
enable = true;
checkConfig = false;
settings = {
audio = {
enable_sounds = true;
};
bar = {
default = {
background_opacity = 0.8;
capsule = true;
capsule_border = "outline";
font_family = "JetBrainsMono Nerd Font Mono";
position = "top";
};
};
desktop_widgets = {
grid = {
cell_size = 16;
major_interval = 4;
visible = true;
};
schema_version = 2;
widget = { };
widget_order = [ ];
};
};
};
nmt.script = ''
local configFile="home-files/.config/noctalia/config.toml"
assertFileExists $configFile
assertFileContent $configFile ${./expected-example-config.toml}
'';
}

View File

@@ -0,0 +1,94 @@
{
"dark": {
"mError": "#ea6962",
"mHover": "#89b482",
"mOnError": "#141617",
"mOnHover": "#141617",
"mOnPrimary": "#141617",
"mOnSecondary": "#141617",
"mOnSurface": "#ddc7a1",
"mOnSurfaceVariant": "#bdae93",
"mOnTertiary": "#141617",
"mOutline": "#5a524c",
"mPrimary": "#d3869b",
"mSecondary": "#7daea3",
"mShadow": "#141617",
"mSurface": "#141617",
"mSurfaceVariant": "#1d2021",
"mTertiary": "#89b482",
"terminal": {
"background": "#141617",
"bright": {
"black": "#5a524c",
"blue": "#7daea3",
"cyan": "#89b482",
"green": "#a9b665",
"magenta": "#d3869b",
"red": "#ea6962",
"white": "#ebdbb2",
"yellow": "#d8a657"
},
"cursor": "#ddc7a1",
"cursorText": "#141617",
"foreground": "#ddc7a1",
"normal": {
"black": "#1d2021",
"blue": "#7daea3",
"cyan": "#89b482",
"green": "#a9b665",
"magenta": "#d3869b",
"red": "#ea6962",
"white": "#ddc7a1",
"yellow": "#d8a657"
},
"selectionBg": "#5a524c",
"selectionFg": "#ddc7a1"
}
},
"light": {
"mError": "#cc241d",
"mHover": "#427b58",
"mOnError": "#fbf1c7",
"mOnHover": "#fbf1c7",
"mOnPrimary": "#fbf1c7",
"mOnSecondary": "#fbf1c7",
"mOnSurface": "#3c3836",
"mOnSurfaceVariant": "#7c6f64",
"mOnTertiary": "#fbf1c7",
"mOutline": "#bdae93",
"mPrimary": "#b16286",
"mSecondary": "#458588",
"mShadow": "#d5c4a1",
"mSurface": "#fbf1c7",
"mSurfaceVariant": "#f2e5bc",
"mTertiary": "#427b58",
"terminal": {
"background": "#fbf1c7",
"bright": {
"black": "#928374",
"blue": "#076678",
"cyan": "#427b58",
"green": "#79740e",
"magenta": "#8f3f71",
"red": "#9d0006",
"white": "#3c3836",
"yellow": "#b57614"
},
"cursor": "#3c3836",
"cursorText": "#fbf1c7",
"foreground": "#3c3836",
"normal": {
"black": "#fbf1c7",
"blue": "#458588",
"cyan": "#689d6a",
"green": "#98971a",
"magenta": "#b16286",
"red": "#cc241d",
"white": "#7c6f64",
"yellow": "#d79921"
},
"selectionBg": "#3c3836",
"selectionFg": "#fbf1c7"
}
}
}

View File

@@ -0,0 +1,20 @@
[audio]
enable_sounds = true
[bar.default]
background_opacity = 0.8
capsule = true
capsule_border = "outline"
font_family = "JetBrainsMono Nerd Font Mono"
position = "top"
[desktop_widgets]
schema_version = 2
widget_order = []
[desktop_widgets.grid]
cell_size = 16
major_interval = 4
visible = true
[desktop_widgets.widget]

View File

@@ -0,0 +1,26 @@
{ config, ... }: {
programs.noctalia = {
enable = true;
package = config.lib.test.mkStubPackage { outPath = "@noctalia@"; };
systemd.enable = true;
};
nmt.script = ''
assertFileContent \
home-files/.config/systemd/user/noctalia.service \
${builtins.toFile "noctalia.service" ''
[Install]
WantedBy=graphical-session.target
[Service]
ExecStart=@noctalia@/bin/dummy
Restart=on-failure
[Unit]
After=graphical-session.target
Description=Noctalia - A lightweight Wayland shell and bar
Documentation=https://docs.noctalia.dev/v5/
PartOf=graphical-session.target
''}
'';
}