Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hyprland Helpers — Typed Nix API for the Home-Manager Hyprland Module

Purpose

The core.hyprland modules extend the upstream Home-Manager wayland.windowManager.hyprland module with a typed Nix API for window rules, permissions, slide-in popups, input defaults, and Lua config generation. They target the HM-native Lua configuration format (configType = "lua"), which is the repository default.

Entry Point

The module structure is:

default.nix           # Top-level importer
├── permission.nix    # custom-settings.permission
├── slideIn.nix       # custom-settings.slideIn
├── input.nix         # settings.config defaults (cursor, binds, input, misc)
├── workspaces.nix    # custom-settings.workspaces
├── lua.nix           # custom-settings.lua (Lua config generation)
│   └── lua/          # Lua source files, with @placeholder@ substitution
│       └── opt/      # Conditionally loaded Lua modules
└── types.nix         # Shared type definitions

Options

wayland.windowManager.hyprland.custom-settings.lua.applicationBinds

Typeattribute set of string
Default{ }

Application binds to generate in Lua config.


wayland.windowManager.hyprland.custom-settings.lua.enable

Typeboolean
Defaultfalse
Exampletrue

Whether to enable Pure Lua configuration files for Hyprland, with a hint of nix substitution magic..


wayland.windowManager.hyprland.custom-settings.lua.luaExtras

Typelist of absolute path
Default[ ]

Extra Lua files to copy to the config directory. These files will be copied to the config directory but not required in init.lua, so you can use them as libraries or for other purposes.

Files defined here will respect parent directories and will be copied to the same relative path in the config directory. The absolute root of the directory tree will be calculated by finding the closest lua ancestor directory, and copying the entire tree from that root to the config directory.

If a directory is specified, it will be recursively copied to the config directory, preserving the directory structure.


wayland.windowManager.hyprland.custom-settings.lua.luaModules

Typelist of absolute path
Default[ ]

Lua modules to load in the main init.lua file. Each module is a path to a Lua file, which will be copied to the config directory and required in init.lua. Each module will have variables substituted according to the “variables” option, so you can use that to inject paths to nix packages or other dynamic values.


wayland.windowManager.hyprland.custom-settings.lua.variables

Typeattribute set of (null or string)
Default{ }

Variables to substitute in Lua files. Each key “foo” replaces @foo@ in source files with the value.


wayland.windowManager.hyprland.custom-settings.permission.plugin

Typelist of (package or string)
Default[ ]

List of plugins that are allowed to run.


wayland.windowManager.hyprland.custom-settings.permission.screenCopy

Typelist of (package or string)
Default[ ]

List of applications allowed to copy the screen.


wayland.windowManager.hyprland.custom-settings.slideIn

Typelist of (submodule)
Default[ ]

List of slide-in popups that slide in from the edge of the screen.


wayland.windowManager.hyprland.custom-settings.slideIn.*.bind

Typestring

Key binding to trigger the slide-in popup.

This is passed through to the lua config without checking for validity, the lua config will throw an error if the binding is invalid.


wayland.windowManager.hyprland.custom-settings.slideIn.*.class

Typestring

Window class for the slide-in popup.


wayland.windowManager.hyprland.custom-settings.slideIn.*.exec

Typestring

Command to execute for the slide-in popup.


wayland.windowManager.hyprland.custom-settings.slideIn.*.extProp

Typeattribute set of anything
Default{ }

Extra properties to pass to the scratchpad configuration in pyprland.


wayland.windowManager.hyprland.custom-settings.slideIn.*.position

Typeone of "left", "right", "top", "bottom"
Default"top"

Direction from which the popup slides in.


wayland.windowManager.hyprland.custom-settings.slideIn.*.size

Typesubmodule
Default{ }

Size of the slide-in popup. Can specify ‘width’ and/or ‘height’.


wayland.windowManager.hyprland.custom-settings.slideIn.*.size.height

Typenull or string
Defaultnull

Height of the slide-in popup. Can be specified as a percentage (e.g., ‘33%’) or in pixels (e.g., ‘300px’).


wayland.windowManager.hyprland.custom-settings.slideIn.*.size.width

Typenull or string
Defaultnull

Width of the slide-in popup. Can be specified as a percentage (e.g., ‘20%’) or in pixels (e.g., ‘400px’).


wayland.windowManager.hyprland.custom-settings.workspaces.definitions

Typeattribute set of (submodule)
Default{ }

Workspace definitions by ID


wayland.windowManager.hyprland.custom-settings.workspaces.definitions.<name>.extraRules

Typeattribute set of anything
Default{ }

Additional workspace rule properties to pass to workspace_rule().


wayland.windowManager.hyprland.custom-settings.workspaces.definitions.<name>.monitor

Typenull or string
Defaultnull

Monitor to assign workspace on startup.


wayland.windowManager.hyprland.custom-settings.workspaces.definitions.<name>.name

Typenull or string
Defaultnull

Workspace default name.


wayland.windowManager.hyprland.custom-settings.workspaces.definitions.<name>.startup

Typelist of (string or attribute set of string)
Default[ ]

Commands to run when workspace is first created empty.


wayland.windowManager.hyprland.custom-settings.workspaces.enable

Typeboolean
Defaultfalse

Enable workspace configuration module.


Architecture / Services / Scope

input.nix

Sets sensible default values under settings.config for cursor behavior, input device settings, keyboard binds, and misc Hyprland options.

Default config covers:

  • cursor: warp behavior, hardware cursors, inactivity timeout, hide-on-key-press
  • binds: workspace back-and-forth, allow workspace cycles, focus method
  • input: keyboard layout, follow-mouse, touchpad, sensitivity, accel profile
  • misc: DPMS on key/mouse events

permission.nix

Defines custom-settings.permission for screen copy and plugin permission grants:

custom-settings.permission = {
  screenCopy = [ pkgs.firefox pkgs.obs ];
  plugin = [ pkgs.hyprlandPlugins.hy3 ];
};

slideIn.nix

Add the option custom-settings.slideIn to define a list of edge-sliding popup windows. Each entry configures a keybind, executable, window class, position, and optional window rules. Uses Pyprland scratchpads for dropdown-style window management.

Architecture:

  • Nix module: generates ~/.config/pypr/config.toml with scratchpad definitions, registers systemd.user.services.pyprland.
  • Lua module: registers hl.bind(...) calls that toggle the scratchpads.

lua.nix

Defines custom-settings.lua — the Lua config generation subsystem:

  • enable (boolean) — Enable pure Lua configuration files with Nix substitution support.
  • variables (attrs of nullOr str) — Key-value pairs for @placeholder@ substitution in Lua source files. Each key foo replaces @foo@ only in Lua modules that reference that placeholder. Modules that don’t reference a given placeholder are unaffected, so bundled modules can use disjoint placeholder sets. A placeholder referenced by a module but absent from variables is an error. Some variables are pre-populated automatically (see applicationBinds below). Common injected values include paths to playerctl, wpctl, zenity, hyprshutdown, and uwsm-app.
  • luaModules (list of paths) — Lua source files to copy into the Hyprland config directory and require from init.lua. Each file undergoes @placeholder@ substitution using the variables attrset. Defaults to the bundled lua/binds.lua.
  • applicationBinds (attrs of str) — Application keybinds passed into Lua generation. Each attr key is a bind string and each attr value is a command string. Rendered into @applicationBinds@ as Lua table entries consumed by binds.lua. Generated Lua iterates over those table entries and creates hl.bind(..., hl.dsp.exec_cmd(...)) calls for each bind/command pair.

Lua bind pattern

In lua/binds.lua, binds use the inline Lua expression pattern via settings.bind with attrsToLuaInlineArgs. The generated Lua calls hl.bind(...) with first-class dispatcher functions:

hl.bind("SUPER + Q", hl.dsp.window.kill())
hl.bind("SUPER + SHIFT + SPACE", hl.dsp.window.float({ action = "toggle" }))
hl.bind("ALT + R", hl.dsp.submap("resize"))
hl.define_submap("resize", function()
  hl.bind("ESCAPE", hl.dsp.submap("reset"))
  -- ...
end)

This pattern keeps bind and submap definitions inline in Lua. Submaps are defined via hl.define_submap(name, fn) alongside related hl.bind(...) calls.

workspaces.nix

Workspace configuration module at modules/home-manager/core/hyprland/workspaces.nix. Handles workspace naming, monitor assignments, and startup applications with startup-only monitor assignment behavior:

  • Startup-only assignment: Monitor assignments run once via hl.on("hyprland.start") Lua event hook. Users can move workspaces freely after startup without interference.
  • Persistent naming: Workspace names and startup commands persist via hl.workspace_rule(), ensuring defaults are restored if a workspace is recreated.
  • Conditional Lua module: The lua/opt/workspaces.lua module is automatically added to the Lua config only when custom-settings.workspaces.enable = true. If disabled, no workspace Lua code is loaded.
  • Configuration source: Workspace data (name, monitor, startup commands) is defined in user configs via the typed custom-settings.workspaces.definitions option and passed to Lua as @workspaceConfig@ variable.

Enable and configure workspaces in user home config:

wayland.windowManager.hyprland.custom-settings.workspaces = {
  enable = true;
  definitions = {
    "1" = {
      name = "Terminal";
      monitor = "DP-6";  # startup-only; workspace can be moved after launch
      startup = [ (lib.getExe pkgs.alacritty) ];  # runs when workspace first created
    };
    "2" = {
      name = "Browser";
      monitor = "DP-1";
      startup = [ (lib.getExe config.programs.firefox.package) ];
    };
    # ...
  };
};

The module generates:

  • Lua substitution variable @workspaceConfig@ with workspace definitions as JSON
  • Conditionally loads lua/opt/workspaces.lua to register startup hooks and workspace rules

lua/opt/workspaces.lua

Lua module at modules/home-manager/core/hyprland/lua/opt/workspaces.lua that registers workspace configuration. Only loaded when custom-settings.workspaces.enable = true. Receives workspace data via @workspaceConfig@ placeholder substitution and:

  • Registers hl.on("hyprland.start") hook to assign workspaces to monitors on startup only
  • Calls hl.workspace_rule() for each workspace to set persistent names and startup commands
PlaceholderSourceDescription
@workspaceConfig@custom-settings.workspaces.definitionsWorkspace config table as JSON

lua/binds.lua

The default Lua bind template at modules/home-manager/core/hyprland/lua/binds.lua. Uses @placeholder@ substitution for dynamic injection. Substitution is per-file — only placeholders actually present in this template are replaced; other Lua modules are unaffected by binds.lua’s placeholder set.

PlaceholderSourceDescription
@applicationBinds@custom-settings.lua.applicationBindsAuto-generated Lua table of app keybinds
@playerctl@Auto-injectedPath to playerctl binary
@wpctl@Auto-injectedPath to wpctl binary
@zenity@Auto-injectedPath to zenity binary
@hyprshutdown@Auto-injectedPath to hyprshutdown binary
@uwsmApp@Auto-injectedPath to uwsm-app helper
@DEFAULT_AUDIO_SINK@custom-settings.lua.variablesAudio sink name
@DEFAULT_AUDIO_SOURCE@custom-settings.lua.variablesAudio source name

Add custom placeholders by extending custom-settings.lua.variables.

noctalia.nix

Integrates the Noctalia desktop shell as a Hyprland companion. Requires the noctalia flake input.

The module:

  • Enables programs.noctalia and systemd, pins package from the noctalia flake input’s packages, and applies Hyprland support for Noctalia windows.
  • Mirrors a full exported Noctalia config as a typed Nix attrset (noctaliaSettings), covering bar layouts with monitor overrides, shell panel/screen corners/screenshot/session actions, theme, wallpaper, calendar, control-center shortcuts, desktop/lockscreen widgets, notification layer, plugin settings, widget config, brightness, and more.
  • Does not declare top-level colors or plugins HM options, and does not manage raw JSON files directly.
  • Persists ~/.local/share/noctalia via user.persistence.directories.
  • Reads core.profile.avatar.path → shell.avatar_path and core.profile.wallpaper.directory → wallpaper.directory. Wallpaper fill mode is hardcoded (not a profile option).
  • Location is driven by core.profile.location.secret (a SOPS secret name). Two modes:
    • Normal (secret == null): sets programs.noctalia.settings. No location block.
    • Secret (secret != null): base TOML generated at build time; activation copies it to ~/.config/noctalia/config.toml and appends [location] address from the decrypted SOPS secret.

The user-side Hyprland config pairs with this module via Noctalia IPC keybinds (fullscreen, special workspace toggles, settings, audio/brightness dispatchers). Workspace rules in the user config set persistent = true for defined workspaces so they are always available regardless of Noctalia lifecycle.

types.nix

Shared type definitions used across the modules:

  • monitorSelector — typed Nix attrs for monitor matching (by name or index)
  • workspaceSelector — typed Nix attrs for workspace matching (by id, relativeId, name, or special)
  • rule — all typed window rule properties (float, fullscreen, opacity, size, move, center, monitor, workspace, and dozens more)
  • windowMatch — match condition types (class, title, initialClass, initialTitle, tag, xwayland, float, fullscreen, pin, focus, group, modal, fullscreenstate, workspace, content, xdg_tag)

Usage Example

{
  wayland.windowManager.hyprland = {
    enable = true;
    configType = "lua";

    custom-settings = {
      permission = {
        screenCopy = [ pkgs.firefox ];
      };

      lua = {
        enable = true;
        luaModules = [ ./lua/window_rules.lua ];
        applicationBinds = {
          "SUPER + Return" = "${pkgs.kitty}/bin/kitty";
          "SUPER + E" = "${pkgs.nautilus}/bin/nautilus";
        };
      };
    };
  };
}

Operational Notes / Assumptions

  • All options live under custom-settings to avoid collision with upstream HM Hyprland options.
  • lua.nix auto-injects applicationBinds, playerctl, wpctl, zenity, hyprshutdown, and uwsmApp as substitution variables — no need to set those manually.
  • Variable substitution is per-file: each Lua module only receives replacements for @placeholder@ tokens it actually contains. A variable defined but unused by a given module is silently ignored for that module. A placeholder referenced by a module but missing from variables is an error.
  • Unknown dispatchers in Lua raise a runtime error from Hyprland’s Lua parser, not a build-time error.
  • CamelCase naming in Nix (e.g. fullscreenState, idleInhibit, keepAspectRatio, noCloseFor, forceRgbx, syncFullscreen) is translated to snake_case in the Lua output.