self: { config, lib, pkgs, ... }: let cfg = config.programs.nova-shell; in { options.programs.nova-shell = { enable = lib.mkEnableOption "nova-shell Quickshell bar"; package = lib.mkOption { type = lib.types.package; default = self.packages.${pkgs.stdenv.hostPlatform.system}.default; description = "nova-shell package to use."; }; modules = let moduleOpt = name: extra: lib.mkOption { default = { }; description = "Configuration for the ${name} module."; type = lib.types.submodule { options = { enable = lib.mkOption { type = lib.types.bool; default = true; description = "Enable the ${name} module."; }; } // extra; }; }; intervalOpt = default: { interval = lib.mkOption { type = lib.types.int; inherit default; description = "Polling interval in milliseconds."; }; }; simpleModules = lib.genAttrs [ "workspaces" "tray" "windowTitle" "clock" "mpris" "volume" "bluetooth" "network" "powerProfile" "cpu" "memory" "idleInhibitor" "privacy" "screenCorners" "power" "backgroundOverlay" "overviewBackdrop" ] (name: moduleOpt name { }); in simpleModules // { lock = moduleOpt "lock" { screenshot = lib.mkOption { type = lib.types.bool; default = true; description = "Show blurred desktop screenshot as lock screen background."; }; notifications = lib.mkOption { type = lib.types.bool; default = true; description = "Show notification app icons on the lock screen."; }; mpris = lib.mkOption { type = lib.types.bool; default = true; description = "Show media controls on the lock screen."; }; volume = lib.mkOption { type = lib.types.bool; default = true; description = "Show volume slider on the lock screen."; }; weather = lib.mkOption { type = lib.types.bool; default = true; description = "Show weather summary on the lock screen."; }; threatEffect = lib.mkOption { type = lib.types.bool; default = true; description = "Show red vignette and chromatic aberration on failed password attempts."; }; }; notifications = moduleOpt "notifications" { timeout = lib.mkOption { type = lib.types.int; default = 3000; description = "Notification popup timeout in milliseconds."; }; maxPopups = lib.mkOption { type = lib.types.int; default = 4; description = "Maximum number of notification popups shown simultaneously."; }; maxVisible = lib.mkOption { type = lib.types.int; default = 10; description = "Maximum visible notifications in the notification center before scrolling."; }; maxHistory = lib.mkOption { type = lib.types.int; default = -1; description = "Maximum notifications kept in history (-1 for unlimited)."; }; }; disk = moduleOpt "disk" ( (intervalOpt 30000) // { warnThreshold = lib.mkOption { type = lib.types.int; default = 85; description = "Usage percentage at which the disk module bar turns warning color."; }; } ); backlight = moduleOpt "backlight" { step = lib.mkOption { type = lib.types.int; default = 5; description = "Brightness adjustment step (%)."; }; }; gpu = moduleOpt "gpu" { warm = lib.mkOption { type = lib.types.int; default = 70; description = "GPU temperature threshold for warm state (°C)."; }; hot = lib.mkOption { type = lib.types.int; default = 85; description = "GPU temperature threshold for hot state (°C)."; }; }; temperature = moduleOpt "temperature" { warm = lib.mkOption { type = lib.types.int; default = 80; description = "Temperature threshold for warm state (°C)."; }; hot = lib.mkOption { type = lib.types.int; default = 90; description = "Temperature threshold for hot state (°C)."; }; device = lib.mkOption { type = lib.types.str; default = ""; description = '' Thermal zone device name to use for the primary temperature reading (e.g. "x86_pkg_temp", "acpitz"). Leave empty to use the system maximum. ''; }; }; battery = moduleOpt "battery" { warning = lib.mkOption { type = lib.types.int; default = 25; description = "Battery percentage for warning notification."; }; critical = lib.mkOption { type = lib.types.int; default = 15; description = "Battery percentage for critical notification and blink."; }; poweralertd = lib.mkOption { type = lib.types.bool; default = true; description = "Enable poweralertd for battery/power event notifications. Set to false to disable."; }; }; weather = moduleOpt "weather" ( (intervalOpt 3600000) // { args = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "--nerd" ]; description = "Arguments passed to wttrbar."; example = [ "--nerd" "--location" "Berlin" ]; }; } ); dock = moduleOpt "dock" { width = lib.mkOption { type = lib.types.int; default = 300; description = "Dock panel width in pixels."; }; applets = lib.mkOption { default = { }; description = "Per-applet enable flags for the dock (independent from bar modules)."; type = lib.types.submodule { options = lib.genAttrs [ "clock" "cpu" "gpu" "memory" "temperature" "disk" "battery" "network" "bluetooth" "volume" "backlight" "weather" "mpris" "notifications" "power" ] ( name: lib.mkOption { type = lib.types.bool; default = true; description = "Show ${name} applet in the dock."; } ); }; }; }; statsDaemon = lib.mkOption { default = { }; description = "Configuration for the nova-stats daemon."; type = lib.types.submodule { options.interval = lib.mkOption { type = lib.types.int; default = -1; description = "nova-stats polling interval in milliseconds (-1 = use binary default of 1s)."; }; }; }; }; theme = lib.mkOption { type = lib.types.attrsOf lib.types.anything; default = { }; description = '' Theme overrides written to `$XDG_CONFIG_HOME/nova-shell/theme.json`. Keys: colors (base00-base0F), fontFamily, iconFontFamily, fontSize, barOpacity, barHeight, barPadding, groupSpacing, groupPadding, moduleSpacing, radius, screenRadius, reducedMotion. Populated from stylix when homeModules.stylix is imported. ''; }; systemd = { enable = lib.mkOption { type = lib.types.bool; default = true; description = "Run nova-shell as a systemd user service."; }; target = lib.mkOption { type = lib.types.str; default = "graphical-session.target"; description = "Systemd target to bind the service to."; }; }; }; config = lib.mkIf cfg.enable { home.packages = [ cfg.package pkgs.nerd-fonts.symbols-only ] ++ lib.optional cfg.modules.weather.enable pkgs.wttrbar ++ lib.optional cfg.modules.mpris.enable pkgs.cava ++ lib.optional cfg.modules.disk.enable pkgs.coreutils; xdg.configFile."nova-shell/modules.json".source = (pkgs.formats.json { }).generate "nova-shell-modules.json" cfg.modules; xdg.configFile."nova-shell/theme.json".source = (pkgs.formats.json { }).generate "nova-shell-theme.json" cfg.theme; # Niri layer rules for backdrop placement (requires niri-flake hm module) programs.niri.settings.layer-rules = lib.mkIf (config ? programs && config.programs ? niri) [ { matches = [ { namespace = "^nova-overview-backdrop$"; } ]; place-within-backdrop = true; } ]; services.poweralertd.enable = lib.mkIf ( cfg.modules.battery.enable && cfg.modules.battery.poweralertd ) (lib.mkDefault true); systemd.user.services.nova-shell = lib.mkIf cfg.systemd.enable { Unit = { Description = "nova-shell Quickshell bar"; PartOf = [ cfg.systemd.target ]; After = [ cfg.systemd.target ]; StartLimitIntervalSec = 30; StartLimitBurst = 5; }; Service = { ExecStart = lib.getExe cfg.package; Restart = "always"; RestartSec = 1; Slice = "session.slice"; }; Install.WantedBy = [ cfg.systemd.target ]; }; }; }