Files
nixos/docs/superpowers/plans/2026-07-24-dank-material-shell.md

13 KiB

DankMaterialShell (niri) Profile Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add a my.profiles.dank NixOS profile that runs DankMaterialShell (DMS) on the niri Wayland compositor as a login session parallel to KDE on mibook, backed by a new opt-in my.profiles.home-manager module.

Architecture: Three flake inputs (home-manager, niri = niri-flake, dank = DankMaterialShell) are added to flake.nix. A my.profiles.home-manager module unconditionally imports the home-manager NixOS module and, when enabled, sets global HM settings. A my.profiles.dank module enables my.profiles.home-manager as a dependency, turns on niri (system session) + the DMS nixosModule (daemons/packages), and configures home-manager.users.finn with the DMS + niri-flake HM modules for keybinds/spawn. mibook enables dank.

Tech Stack: Nix flakes, flake-parts, NixOS modules, home-manager (as NixOS module), niri-flake, DankMaterialShell flake, quickshell (from pkgs.unstable).

Global Constraints

  • Do not modify machines/configuration.nix. inputs and self are already threaded into every module via _module.args; consume them as module arguments.
  • Top-level imports must reference flake modules via self.inputs.*, not inputs.*. self is a specialArg (available during imports resolution); inputs comes from _module.args (config-derived) and using it in an imports list causes infinite recursion. Inside the config body (including nested home-manager.users.<name>.imports), either inputs or self.inputs is fine.
  • Base channel is pinned nixpkgs/nixos-26.05; pkgs.unstable overlay is available everywhere. quickshell must come from pkgs.unstable.quickshell (needs ≥ 0.3.0).
  • home-manager scoped to user finn only; home.stateVersion = "26.05".
  • Follow the existing profile pattern exactly: let cfg = config.my.profiles.<name>; in { options.my.profiles.<name>.enable = lib.mkEnableOption "..."; config = lib.mkIf cfg.enable { ... }; }.
  • Format every new/edited .nix file with nixfmt-rfc-style before committing.
  • Commit messages end with the repo's trailer lines (Co-Authored-By + Claude-Session) as seen in recent history.
  • KDE (my.profiles.kde-desktop) stays enabled on mibook; do not remove it.

Task 1: Add flake inputs

Files:

  • Modify: flake.nix:4-15 (the inputs = { ... } block)

Interfaces:

  • Produces: flake inputs inputs.home-manager, inputs.niri, inputs.dank, reachable from any NixOS module (via the existing _module.args.inputs = self.inputs). Later tasks consume inputs.home-manager.nixosModules.home-manager, inputs.niri.homeModules.niri, inputs.dank.nixosModules.dank-material-shell, inputs.dank.homeModules.dank-material-shell, inputs.dank.homeModules.niri.

  • Step 1: Add the three inputs

In flake.nix, inside the inputs = { ... } block, after the nixos-generators entry and before the closing };, add:

    home-manager = {
      url = "github:nix-community/home-manager/release-26.05";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    niri.url = "github:sodiboo/niri-flake";
    dank.url = "github:AvengeMedia/DankMaterialShell";
  • Step 2: Resolve the lock file

Run: nix flake lock Expected: completes without error; flake.lock gains home-manager, niri, niri-flake-transitive, and dank (DankMaterialShell) nodes. If it reports a bad URL, re-check the github:owner/repo strings.

  • Step 3: Verify inputs expose the expected outputs

Run: nix eval --raw '.#nixosConfigurations.mibook.pkgs.system' first to confirm the flake still evaluates (expected: x86_64-linux). Then run: nix flake show 'github:AvengeMedia/DankMaterialShell' --allow-import-from-derivation 2>/dev/null | grep -E 'dank-material-shell|nixosModules|homeModules' Expected: output lists nixosModules.dank-material-shell and homeModules.dank-material-shell and homeModules.niri. If the niri output name differs (e.g. dankMaterialShell.niri), note the actual name — Task 3 Step 1 must use whatever this shows.

  • Step 4: Format and commit
nixfmt-rfc-style flake.nix
git add flake.nix flake.lock
git commit -m "chore: add home-manager, niri-flake, and DankMaterialShell flake inputs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN"

Task 2: my.profiles.home-manager module

Files:

  • Create: modules/environments/home-manager/default.nix
  • Modify: modules/environments/default.nix (add ./home-manager to imports)

Interfaces:

  • Consumes: inputs.home-manager.nixosModules.home-manager (Task 1).

  • Produces: option my.profiles.home-manager.enable. When enabled, sets home-manager.useGlobalPkgs = true, home-manager.useUserPackages = true, home-manager.extraSpecialArgs = { inherit inputs self; }. Also makes the home-manager.users.<name> option available (from the unconditional import) so Task 3 can populate home-manager.users.finn.

  • Step 1: Create the module

Create modules/environments/home-manager/default.nix:

{
  config,
  lib,
  inputs,
  self,
  ...
}:
let
  cfg = config.my.profiles.home-manager;
in
{
  imports = [ self.inputs.home-manager.nixosModules.home-manager ];

  options.my.profiles.home-manager.enable =
    lib.mkEnableOption "home-manager integration";

  config = lib.mkIf cfg.enable {
    home-manager.useGlobalPkgs = true;
    home-manager.useUserPackages = true;
    home-manager.extraSpecialArgs = { inherit inputs self; };
  };
}
  • Step 2: Register the module

In modules/environments/default.nix, add ./home-manager to the imports list (place it after ./hyprland):

    ./hyprland
    ./home-manager
  • Step 3: Verify it evaluates disabled (no behavior change)

Run: nix build '.#nixosConfigurations.mibook.config.system.build.toplevel' --dry-run Expected: evaluates and shows a build plan with no errors. Because my.profiles.home-manager.enable defaults to false, importing the HM module must not change the build outcome.

  • Step 4: Verify the option exists and defaults false

Run: nix eval '.#nixosConfigurations.mibook.config.my.profiles.home-manager.enable' Expected: false

  • Step 5: Format and commit
nixfmt-rfc-style modules/environments/home-manager/default.nix modules/environments/default.nix
git add modules/environments/home-manager/default.nix modules/environments/default.nix
git commit -m "feat(home-manager): add opt-in my.profiles.home-manager module

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN"

Task 3: my.profiles.dank module

Files:

  • Create: modules/environments/dank/default.nix
  • Modify: modules/environments/default.nix (add ./dank to imports)

Interfaces:

  • Consumes: my.profiles.home-manager.enable (Task 2); inputs.dank.nixosModules.dank-material-shell, inputs.dank.homeModules.dank-material-shell, inputs.dank.homeModules.niri, inputs.niri.homeModules.niri (Task 1); pkgs.unstable.quickshell.

  • Produces: option my.profiles.dank.enable.

  • Step 1: Create the module

Create modules/environments/dank/default.nix. Use the exact homeModules/nixosModules output names confirmed in Task 1 Step 3 — the block below assumes dank-material-shell and niri:

{
  config,
  lib,
  pkgs,
  inputs,
  self,
  ...
}:
let
  cfg = config.my.profiles.dank;
in
{
  imports = [ self.inputs.dank.nixosModules.dank-material-shell ];

  options.my.profiles.dank.enable = lib.mkEnableOption "DankMaterialShell on niri";

  config = lib.mkIf cfg.enable {
    # Dependency: declarative user config comes from home-manager.
    my.profiles.home-manager.enable = true;

    # System side: niri Wayland session + DMS daemons/packages.
    programs.niri.enable = true;

    programs.dank-material-shell = {
      enable = true;
      systemd.enable = true;
      quickshell.package = pkgs.unstable.quickshell;
    };

    # Home side: DMS shell + niri spawn for finn (keybinds come via includes).
    home-manager.users.finn = {
      imports = [
        inputs.niri.homeModules.niri
        inputs.dank.homeModules.dank-material-shell
        inputs.dank.homeModules.niri
      ];

      programs.dank-material-shell = {
        enable = true;
        systemd.enable = true;
        quickshell.package = pkgs.unstable.quickshell;
        # niri.enableKeybinds intentionally omitted: DMS's niri `includes.enable`
        # (default true) already brings in the generated binds.kdl plus theming;
        # setting enableKeybinds too triggers an upstream "not recommended" warning.
        niri.enableSpawn = true;
      };

      home.stateVersion = "26.05";
    };
  };
}
  • Step 2: Register the module

In modules/environments/default.nix, add ./dank to the imports list (after ./home-manager):

    ./home-manager
    ./dank
  • Step 3: Verify it evaluates disabled

Run: nix eval '.#nixosConfigurations.mibook.config.my.profiles.dank.enable' Expected: false Run: nix build '.#nixosConfigurations.mibook.config.system.build.toplevel' --dry-run Expected: evaluates without error (dank disabled → no behavior change yet).

  • Step 4: Format and commit
nixfmt-rfc-style modules/environments/dank/default.nix modules/environments/default.nix
git add modules/environments/dank/default.nix modules/environments/default.nix
git commit -m "feat(dank): add DankMaterialShell (niri) profile

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN"

Task 4: Enable on mibook and build the full toplevel

Files:

  • Modify: machines/mibook/environments.nix:7-17 (the my.profiles = { ... } block)

Interfaces:

  • Consumes: my.profiles.dank.enable (Task 3).

  • Step 1: Enable the profile

In machines/mibook/environments.nix, inside the my.profiles = { ... } block, add:

    dank.enable = true;
  • Step 2: Build the full toplevel (the real test)

Run: nix build '.#nixosConfigurations.mibook.config.system.build.toplevel' Expected: builds successfully. This exercises the DMS package, quickshell from unstable, niri, and the home-manager generation for finn.

If it fails on a quickshell/Qt6 version mismatch (see spec risks): drop quickshell.package = pkgs.unstable.quickshell; from both the system and HM programs.dank-material-shell blocks in modules/environments/dank/default.nix and rebuild, letting DMS's module pick its own default quickshell. If it fails on an unknown HM option (e.g. niri.enableKeybinds), run nix eval '.#nixosConfigurations.mibook.config.home-manager.users.finn.programs.dank-material-shell' --apply builtins.attrNames 2>&1 | head to list the real option names and adjust. Re-run the build until it passes.

  • Step 3: Run flake check

Run: nix flake check Expected: passes (all nixosConfigurations evaluate). If nix flake check is slow or pulls DMS's own checks, nix build '.#nixosConfigurations.mibook.config.system.build.toplevel' from Step 2 passing is the authoritative gate.

  • Step 4: Format and commit
nixfmt-rfc-style machines/mibook/environments.nix
git add machines/mibook/environments.nix modules/environments/dank/default.nix
git commit -m "feat(mibook): enable DankMaterialShell (niri) session

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN"
  • Step 5: Manual acceptance (human, on hardware)

Run: sudo nixos-rebuild test --flake '.#mibook' Then log out, pick the niri session at SDDM, log in as finn. Expected: DMS bar/shell appears; Mod+Space opens the launcher; Mod+V opens the clipboard; the KDE session is still selectable and works. Note this step cannot be automated — it requires the physical machine.


Notes for the implementer

  • Order matters: Task 1 must land first (inputs must resolve before any module can import them). Tasks 2→3→4 are strictly sequential.
  • Output-name drift is the top risk. The DMS flake has renamed outputs over time (there are deprecated aliases like dankMaterialShell.niri). Task 1 Step 3 pins down the real names against the locked revision; use those, not the names in this plan, if they differ.
  • No unit-test framework here. Nix evaluation + a successful toplevel build is the test. Do not fabricate a test harness.
  • Do not touch machines/configuration.nix, machines/jupiter/*, or the KDE profile.