{ inputs, config, pkgs, lib, osConfig ? { }, ... }: let home = config.home.homeDirectory; dmsEnabled = lib.attrByPath [ "chiasson" "desktop" "shell" ] null osConfig == "dms"; dmsOs = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" ] { } osConfig; gpuTempEnabled = dmsOs.enableGpuTemp or true; hyprlandEnabled = lib.attrByPath [ "chiasson" "desktop" "hyprland" "enable" ] false osConfig; discordEnabled = lib.attrByPath [ "chiasson" "home" "apps" "discord" "enable" ] false config; ohMyPoshEnabled = config.programs.oh-my-posh.enable or false; obsidianSnippetDirs = map (v: v + "/snippets") (dmsOs.obsidianVaults or [ ]); seedBarWidgets = lib.optionals (dmsOs.enableWvkbdToggle or false) [ { id = "wvkbdToggle"; enabled = true; } ] ++ lib.optionals (dmsOs.enableRbwLockToggle or false) [ { id = "rbwLockToggle"; enabled = true; } ] ++ (dmsOs.extraRightBarWidgets or [ ]); matugenOutputDirs = lib.optionals hyprlandEnabled [ "${home}/.config/hypr" ] ++ lib.optionals ohMyPoshEnabled [ "${home}/.config/oh-my-posh" ] ++ lib.optionals discordEnabled [ "${home}/.config/vesktop/themes" ] ++ [ "${home}/.config/qt6ct/colors" "${home}/.config/qt5ct/colors" ]; ensureMatugenOutputDirsScript = pkgs.writeShellScript "dms-ensure-matugen-output-dirs" '' set -euo pipefail ${lib.concatStringsSep "\n" (map (dir: "mkdir -p ${lib.escapeShellArg dir}") matugenOutputDirs)} ${lib.concatStringsSep "\n" (map (dir: '' snippetDir=${lib.escapeShellArg dir} parentDir="$(dirname "$snippetDir")" if [ -d "$snippetDir" ]; then : elif [ ! -d "$parentDir" ] || [ ! -w "$parentDir" ]; then echo "dms: skipping Obsidian snippet dir '$snippetDir' (parent missing or not writable)" >&2 else mkdir -p "$snippetDir" fi '') obsidianSnippetDirs)} ${lib.optionalString ohMyPoshEnabled '' dest="$HOME/.config/oh-my-posh/theme.omp.json" template="$HOME/.config/matugen/templates/ohmyposh-theme.omp.json" if [ -L "$dest" ] || { [ -e "$dest" ] && ! [ -f "$dest" ]; }; then rm -f "$dest" fi if [ ! -f "$dest" ]; then cp -L "$template" "$dest" fi ''} ''; # Clears Quickshell QML bytecode cache so bar plugins reload (stale *.qmlc keeps old UI). dmsRestartOnceScript = pkgs.writeShellScript "dms-rbw-plugin-restart-once" '' set -euo pipefail rt="''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" exec 9>"$rt/dms-rbw-plugin-restart.lock" ${pkgs.util-linux}/bin/flock -n 9 || exit 0 cache="''${XDG_CACHE_HOME:-''$HOME/.cache}/quickshell/qmlcache" rm -rf "$cache" ${pkgs.systemd}/bin/systemctl --user try-restart dms.service 2>/dev/null || true ''; jsonFormat = pkgs.formats.json { }; dmsConfigDir = "${config.xdg.configHome}/DankMaterialShell"; defaultSeedDir = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" "defaultSeedDir" ] null osConfig; dmsSeedSources = if defaultSeedDir != null then { "settings.json" = defaultSeedDir + "/settings.json"; } // lib.optionalAttrs (builtins.pathExists (defaultSeedDir + "/plugin_settings.json")) { "plugin_settings.json" = defaultSeedDir + "/plugin_settings.json"; } else { "settings.json" = jsonFormat.generate "dms-settings-seed.json" ( import ../_private/default-settings.nix { inherit lib gpuTempEnabled; extraRightBarWidgets = seedBarWidgets; } ); }; # Directory name must match each plugin's `id` in plugin.json. thirdPartyPlugins = { dankVault = inputs.dms-plugin-dank-vault; calculator = inputs.dms-plugin-calculator; homeAssistantMonitor = inputs.dms-plugin-home-assistant; dropdownMenu = inputs.dms-plugin-dropdown-menu; ocrScanner = inputs.dms-plugin-ocr-scanner; unifiedTaskbar = inputs.dms-plugin-unified-taskbar; widgetGroup = inputs.dms-plugin-widget-group; nixPackageRunner = inputs.dms-plugin-nix-package-runner; ambientSound = inputs.dms-plugin-ambient-sound; cavaVisualizer = inputs.dms-plugin-cava-visualizer; emojiLauncher = inputs.dms-plugin-emoji-launcher; dankDesktopWeather = inputs.dms-plugin-official + "/DankDesktopWeather"; webSearch = inputs.dms-plugin-web-search; wallpaperCarousel = inputs.dms-plugin-wallpaper-carousel; ephemera = inputs.dms-plugin-ephemera; appShortcut = inputs.dms-plugin-app-shortcut; }; shipThirdPartyPlugin = id: src: { source = src; onChange = "${dmsRestartOnceScript}"; }; in { imports = [ inputs.dms.homeModules.dank-material-shell inputs.nix-monitor.homeManagerModules.default ]; config = lib.mkIf dmsEnabled (lib.mkMerge [ (lib.mkIf (dmsOs.enableWvkbdToggle or false) { xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/plugin.json".source = ../plugins/wvkbd-toggle/plugin.json; xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/WvkbdToggle.qml".source = ../plugins/wvkbd-toggle/WvkbdToggle.qml; xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/WvkbdToggleSettings.qml".source = ../plugins/wvkbd-toggle/WvkbdToggleSettings.qml; }) (lib.mkIf (dmsOs.enableRbwLockToggle or false) { # Directory name matches plugin id (same idea as NixMonitor / ideapad wvkbdToggle). xdg.configFile."DankMaterialShell/plugins/rbwLockToggle/plugin.json" = { source = ../plugins/rbw-lock-toggle/plugin.json; onChange = "${dmsRestartOnceScript}"; }; xdg.configFile."DankMaterialShell/plugins/rbwLockToggle/RbwLockToggleSettings.qml" = { source = ../plugins/rbw-lock-toggle/RbwLockToggleSettings.qml; onChange = "${dmsRestartOnceScript}"; }; xdg.configFile."DankMaterialShell/plugins/rbwLockToggle/RbwLockToggle.qml" = { source = ../plugins/rbw-lock-toggle/RbwLockToggle.qml; onChange = "${dmsRestartOnceScript}"; }; }) (lib.mkIf (dmsOs.bundleThirdPartyPlugins or true) { xdg.configFile = lib.mapAttrs (id: src: shipThirdPartyPlugin id src) thirdPartyPlugins; }) { # Nix rebuild widget used by DankMaterialShell (via nix-monitor). # Per-host `rebuildCommand` is set in `modules/hosts//configuration.nix` (`chiasson.desktop.shells.dms`). programs.nix-monitor = { enable = true; generationsCommand = [ "bash" "-c" "readlink /nix/var/nix/profiles/system | cut -d- -f2" ]; }; home.packages = with pkgs; [ kdePackages.qtdeclarative kdePackages.kirigami.unwrapped kdePackages.qtmultimedia kdePackages.sonnet # ocrScanner DMS plugin — eng/fra/vie and 120+ langs ship in nixpkgs tessdata. tesseract # ambientSound DMS plugin — mpv playback + socat IPC to mpv socket. mpv socat # dankVault / emojiLauncher / ephemera — Wayland clipboard. wl-clipboard # nixPackageRunner DMS plugin. jq # cavaVisualizer DMS plugin. cava # ephemera DMS plugin — API requests + optional keyring storage. curl libsecret ]; programs.dank-material-shell = { enable = true; # homeAssistantMonitor plugin (QtWebSockets) needs QML on DMS's import path, not just home.packages. package = inputs.dms.packages.${pkgs.stdenv.hostPlatform.system}.dms-shell.override { extraQtPackages = with pkgs.kdePackages; [ qtwebsockets ]; }; systemd = { enable = true; # Systemd service for auto-start restartIfChanged = true; # Auto-restart dms.service when dankMaterialShell changes }; # Core features enableSystemMonitoring = true; # System monitoring widgets (dgop) dgop.package = inputs.dgop.packages.${pkgs.stdenv.hostPlatform.system}.default; enableVPN = true; # VPN management widget enableDynamicTheming = true; # Wallpaper-based theming (matugen) enableAudioWavelength = true; # Audio visualizer (cava) enableCalendarEvents = false; # Disable calendar integration (khal/ikhal) # Leave `session` at {} — non-empty makes HM rewrite session.json every switch and nukes wallpaper/session state. # Leave `settings` at {} — UI-owned settings.json is seeded once on first run (see home.activation below). settings = { }; session = { }; }; # Detach store symlinks so UI edits survive rebuilds; seed defaults only on a fresh profile. home.activation.dmsSeedConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] '' ${lib.concatMapStringsSep "\n" ( fileName: let dest = "${dmsConfigDir}/${fileName}"; src = dmsSeedSources.${fileName}; in '' dest=${lib.escapeShellArg dest} mkdir -p "$(dirname "$dest")" if [ -L "$dest" ]; then ${pkgs.coreutils}/bin/cp --remove-destination -L "$dest" "$dest" fi if [ ! -e "$dest" ]; then ${pkgs.coreutils}/bin/cp ${lib.escapeShellArg src} "$dest" chmod u+w "$dest" fi '' ) (lib.attrNames dmsSeedSources)} ''; # matugen won't create missing output_path parents; run before DMS theme generation. systemd.user.services.dms.serviceConfig.ExecStartPre = lib.mkOrder 100 "${ensureMatugenOutputDirsScript}"; # DMS / matugen custom templates. # # DMS docs (custom templates): https://danklinux.com/docs/dankmaterialshell/application-themes#custom-matugen-templates xdg.configFile."matugen/config.toml".text = '' [config] '' + lib.optionalString hyprlandEnabled '' [templates.hyprland] input_path = '${home}/.config/matugen/templates/hyprland-colors.conf' output_path = '${home}/.config/hypr/colors.conf' '' + lib.optionalString ohMyPoshEnabled '' [templates.ohmyposh] input_path = '${home}/.config/matugen/templates/ohmyposh-theme.omp.json' output_path = '${home}/.config/oh-my-posh/theme.omp.json' '' + lib.optionalString discordEnabled '' [templates.dank-discord] input_path = '${home}/.config/matugen/templates/dank-discord.css' output_path = '${home}/.config/vesktop/themes/dank-discord.css' '' + lib.optionalString (obsidianSnippetDirs != []) ( lib.concatStringsSep "\n" ( lib.lists.imap0 (i: snippetDir: '' [templates.Obsidian${toString i}] input_path = '${home}/.config/matugen/templates/obsidian-minimal-matugen-colors.css' output_path = '${snippetDir}/matugen.css' '') obsidianSnippetDirs ) ) ; xdg.configFile."matugen/templates/dank-discord.css".source = ../templates/dank-discord.css; xdg.configFile."matugen/templates/hyprland-colors.conf".source = ../templates/hyprland-colors.conf; xdg.configFile."matugen/templates/ohmyposh-theme.omp.json".source = ../templates/ohmyposh-theme.omp.json; xdg.configFile."matugen/templates/obsidian-minimal-matugen-colors.css".source = ../templates/obsidian-minimal-matugen-colors.css; } (lib.mkIf ohMyPoshEnabled { # Same path as matugen `[templates.ohmyposh]`; seeded as a real file by `ensureMatugenOutputDirsScript`. programs.oh-my-posh = { useTheme = lib.mkForce null; configFile = lib.mkForce "${config.xdg.configHome}/oh-my-posh/theme.omp.json"; }; }) ]); }