5357ba307b
- Transitioned to an auto-derived plugin registry from flake inputs, simplifying the addition of new plugins by requiring edits only in `flake.nix`. - Implemented a camelCase transformation for plugin IDs to align with upstream conventions, with exceptions for specific plugins. - Enhanced the `shipPlugin` function to route plugin sources correctly, ensuring compatibility with the DMS structure. - Added assertions to validate Obsidian vault paths at build time, improving error handling and user guidance.
277 lines
10 KiB
Nix
277 lines
10 KiB
Nix
# Home Manager wiring for DMS (imported by default.nix; lives under _private/ for import-tree).
|
|
{
|
|
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;
|
|
hostName = lib.attrByPath [ "networking" "hostName" ] "nixos" osConfig;
|
|
rebuildCommand =
|
|
if (dmsOs.rebuildCommand or null) != null then
|
|
dmsOs.rebuildCommand
|
|
else
|
|
[ "sudo" "nixos-rebuild" "switch" "--flake" ".#${hostName}" ];
|
|
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 [ ]);
|
|
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"
|
|
];
|
|
ensureMatugenBody =
|
|
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
|
|
'';
|
|
ensureMatugenOutputDirsScript = pkgs.writeShellScript "dms-ensure-matugen-output-dirs" ''
|
|
set -euo pipefail
|
|
${ensureMatugenBody}
|
|
'';
|
|
|
|
dmsRestartOnceScript = pkgs.writeShellScript "dms-plugin-restart-once" (
|
|
builtins.replaceStrings
|
|
[ "@FLOCK@" "@SYSTEMCTL@" ]
|
|
[
|
|
"${pkgs.util-linux}/bin/flock"
|
|
"${pkgs.systemd}/bin/systemctl"
|
|
]
|
|
(builtins.readFile ../scripts/dms-restart-once.sh)
|
|
);
|
|
|
|
jsonFormat = pkgs.formats.json { };
|
|
dmsConfigDir = "${config.xdg.configHome}/DankMaterialShell";
|
|
defaultSeedDir = dmsOs.defaultSeedDir or null;
|
|
|
|
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 ./default-settings.nix {
|
|
inherit lib gpuTempEnabled;
|
|
extraRightBarWidgets = dmsOs.extraRightBarWidgets or [ ];
|
|
}
|
|
);
|
|
};
|
|
|
|
dmsSeedBody = 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);
|
|
dmsSeedConfigScript = pkgs.writeShellScript "dms-seed-config" ''
|
|
set -euo pipefail
|
|
${dmsSeedBody}
|
|
'';
|
|
|
|
matugenConfigText = import ./matugen-config.nix {
|
|
inherit
|
|
lib
|
|
home
|
|
hyprlandEnabled
|
|
ohMyPoshEnabled
|
|
discordEnabled
|
|
obsidianSnippetDirs
|
|
;
|
|
};
|
|
|
|
# Plugin registry auto-derived from flake inputs matching `dms-plugin-*`.
|
|
# Adding a plugin now requires editing only `flake.nix`. The on-disk
|
|
# plugin id (= path under ~/.config/DankMaterialShell/plugins/) is camelCase
|
|
# per upstream convention; the kebab→camel transform matches it. The
|
|
# `idExceptions` map carries the one plugin whose manifest id does not
|
|
# follow the default rule.
|
|
pluginInputs = lib.filterAttrs (n: _: lib.hasPrefix "dms-plugin-" n) inputs;
|
|
|
|
# `dank-vault` → `dankVault`. `lib.splitString` yields ≥ 1 element so
|
|
# `head parts` is safe; `map` runs over the tail only (no-op for
|
|
# single-word names like `calculator`). `builtins.substring 1 (-1) p`
|
|
# returns the rest of `p` from index 1, dropping the first char
|
|
# (Nix treats a negative `len` as "rest of string from `start`").
|
|
toCamel = s:
|
|
let parts = lib.splitString "-" s; in
|
|
builtins.head parts
|
|
+ lib.concatStrings (map (
|
|
p: if p == "" then ""
|
|
else lib.toUpper (builtins.substring 0 1 p)
|
|
+ builtins.substring 1 (-1) p
|
|
) (builtins.tail parts));
|
|
|
|
# The `home-assistant` plugin ships its manifest under id
|
|
# `homeAssistantMonitor` (not the default kebab→camel result).
|
|
idExceptions = { "home-assistant" = "homeAssistantMonitor"; };
|
|
|
|
# NB: don't rewrite as `idExceptions.${stripped} or toCamel stripped`.
|
|
# Nix grammar: `or` binds tighter than function application, so the
|
|
# `toCamel` and its arg parse as separate postfix args to the select.
|
|
# When the lookup succeeds the resolved string is then applied as a
|
|
# function, producing `attempt to call something which is not a function
|
|
# but a string: "homeAssistantMonitor"`. Explicit `if/hasAttr` sidesteps
|
|
# the parse entirely.
|
|
camelMap = lib.mapAttrs' (n: src:
|
|
let stripped = lib.removePrefix "dms-plugin-" n; in
|
|
lib.nameValuePair
|
|
(if builtins.hasAttr stripped idExceptions
|
|
then idExceptions.${stripped}
|
|
else toCamel stripped)
|
|
src
|
|
) pluginInputs;
|
|
|
|
# The official plugin pack bundles multiple QML apps. Ship only the
|
|
# DankDesktopWeather subdir (verified by its manifest) to slim closure
|
|
# and avoid overlapping with future dedicated `dms-plugin-*` flakes.
|
|
thirdPartyPlugins =
|
|
builtins.removeAttrs camelMap [ "official" ] // {
|
|
dankDesktopWeather = inputs.dms-plugin-official + "/DankDesktopWeather";
|
|
};
|
|
|
|
# DMS reads plugins from ~/.config/DankMaterialShell/plugins/<id>/, so
|
|
# we route each plugin source there via HM's `xdg.configFile.target`.
|
|
# The attrset name is scoped under `dms-plugin-${id}` (not the bare
|
|
# camelCase id) so unrelated HM modules can ship a same-named file
|
|
# (e.g. `xdg.configFile.calculator`) without colliding with us.
|
|
shipPlugin = id: src: {
|
|
source = src;
|
|
target = "DankMaterialShell/plugins/${id}";
|
|
onChange = "${dmsRestartOnceScript}";
|
|
};
|
|
in
|
|
{
|
|
imports = [
|
|
inputs.dms.homeModules.dank-material-shell
|
|
inputs.nix-monitor.homeManagerModules.default
|
|
];
|
|
|
|
config = lib.mkIf dmsEnabled (lib.mkMerge [
|
|
(lib.mkIf (dmsOs.bundleThirdPartyPlugins or true) {
|
|
xdg.configFile = lib.mapAttrs' (id: src: lib.nameValuePair
|
|
"dms-plugin-${id}" (shipPlugin id src)) thirdPartyPlugins;
|
|
})
|
|
{
|
|
programs.nix-monitor = {
|
|
enable = true;
|
|
inherit rebuildCommand;
|
|
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
|
|
tesseract
|
|
mpv
|
|
socat
|
|
wl-clipboard
|
|
jq
|
|
cava
|
|
curl
|
|
libsecret
|
|
];
|
|
|
|
programs.dank-material-shell = {
|
|
enable = true;
|
|
package = inputs.dms.packages.${pkgs.stdenv.hostPlatform.system}.dms-shell.override {
|
|
extraQtPackages = with pkgs.kdePackages; [ qtwebsockets ];
|
|
};
|
|
systemd = {
|
|
enable = true;
|
|
restartIfChanged = true;
|
|
};
|
|
enableSystemMonitoring = true;
|
|
dgop.package = inputs.dgop.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
|
enableVPN = true;
|
|
enableDynamicTheming = true;
|
|
enableAudioWavelength = true;
|
|
enableCalendarEvents = false;
|
|
# Empty on purpose: DMS owns ~/.config/DankMaterialShell/{settings,session}.json
|
|
# at runtime (seeded by `home.activation.dmsSeedConfig`). Setting these
|
|
# to non-empty here would clobber the runtime seed on every activation.
|
|
settings = { };
|
|
session = { };
|
|
};
|
|
|
|
# Surface bad obsidianVaults paths at build time instead of at first matugen run.
|
|
# Each entry must point at the `<vault>/.obsidian` dir (matugen writes
|
|
# `<vault>/.obsidian/snippets/matugen.css` from there).
|
|
assertions = lib.mkIf ((dmsOs.obsidianVaults or [ ]) != [ ]) [
|
|
{
|
|
assertion = builtins.all (v: lib.hasSuffix "/.obsidian" v) (dmsOs.obsidianVaults or [ ]);
|
|
message = "chiasson.desktop.shells.dms.obsidianVaults entries must end in '/.obsidian' (matugen writes <vault>/.obsidian/snippets/matugen.css). Example: /home/olivier/Documents/HomeLab/.obsidian";
|
|
}
|
|
];
|
|
|
|
home.activation.dmsSeedConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] "${dmsSeedConfigScript}";
|
|
|
|
systemd.user.services.dms.serviceConfig.ExecStartPre =
|
|
lib.mkOrder 100 "${ensureMatugenOutputDirsScript}";
|
|
|
|
xdg.configFile."matugen/config.toml".text = matugenConfigText;
|
|
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 {
|
|
programs.oh-my-posh = {
|
|
useTheme = lib.mkForce null;
|
|
configFile = lib.mkForce "${config.xdg.configHome}/oh-my-posh/theme.omp.json";
|
|
};
|
|
})
|
|
]);
|
|
}
|